附加题 DP题目
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; }