http://acm.hdu.edu.cn/diy/contest_showproblem.php?cid=18068&pid=1008
题意:
给你n个数a[n],求从中顺序的选出k个数b[k],这k个数分别进行如下操作,sum = b[1]*1 + b[2]*2 + b[3]*3 + ...... + b[k]*k 求使得sum最小。才开始von给我说可能需要单调队列优化,我看看了,用单调队列做了一下。提交不对,一看状态转移方程推错了。囧....后来一看这不是一个很典型的状态转移方程式吗。以前做过类似的题目
dp[i][j] = min(dp[i - 1][j],dp[i - 1][j - 1] + a[i]*j)
写了个二维的,结果直接MLE,这不科学,计算10^7次不会MLE。于是就去优化。我们可以看出i由i-1推出,所以我们可以把j反向取,把i这一维省去。有点01背包优化的感觉。
注意一下数据范围:
#include <iostream>
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <algorithm>
#include <cmath>
#include <queue>
#include <stack>
#include <set>
#include <map>
#include <string>
#define CL(a,num) memset((a),(num),sizeof(a))
#define iabs(x) ((x) > 0 ? (x) : -(x))
#define Min(a,b) (a) > (b)? (b):(a)
#define Max(a,b) (a) > (b)? (a):(b)
#define ll __int64
#define inf 0x7f7f7f7f
#define MOD 1073741824
#define lc l,m,rt<<1
#define rc m + 1,r,rt<<1|1
#define pi acos(-1.0)
#define test puts("<------------------->")
#define maxn 100007
#define M 500007
#define N 10007
#define K 1007
using namespace std;
//freopen("din.txt","r",stdin);
ll a[N];
ll dp[K];
int main()
{
//freopen("din.txt","r",stdin);
int i,j;
int n,k;
while (~scanf("%d%d",&n,&k))
{
for (i = 1; i <= n; ++i) scanf("%I64d",&a[i]);
for (i = 0; i <= k; ++i) dp[i] = inf;
dp[1] = a[1]; dp[0] = 0;
for (i = 2; i <= n; ++i)
{
for (j = min(i,k); j >= 1; --j)
{
dp[j] = min(dp[j],dp[j - 1] + a[i]*j);
}
/*
for (j = 1; j < i && j <= k; ++j)
{
dp[i][j] = min(dp[i - 1][j],dp[i - 1][j - 1] + a[i]*j);
}
*/
}
printf("%I64d\n",dp[k]);
}
return 0;
}