|
||
|
慢慢啃
f[j][u]表示空间为j时的第u优解;
性质:加了一个u循环操作的01背包,i表示加入第i个物品则循环顺序从外到里为i->j->u(就是把原本f[j]=max(f[j-w[i]]+v[i],f[j])的位置变成u的循环操作而已);
初始化:
f[j][u]=负无穷(u循环操作前要判定f[j-w[i]][1]>=0),f[0][1]=0;
u的循环操作:
1.已知f[j]=max(f[j-w[i]]+v[i],f[j]),即f[j]有两种取值方案;
用两个数组st1[u]和st2[u]分别储存f[j-w[i]][u]+v[i]和f[j][u];
2.因为f[j][u]一定优于f[j][u+1],所以st1[u]一定优于st1[u+1],st2[u]同理,所以只需要比较st1和st2中的元素就可以得到当前可以取到的最优值;
所以f[i][u]等于st1[tail1]和st2[tail2]中较大的;
3.因为不能有一样的方案,所以st1[tail1],st2[tail2]中较大的数被f[i][u]取值后,对应的tail++(tail在u循环开始前定义为0或者1);
随着j的增大,方案数是树状增多的,但是前u个最优方案一定是在这棵树的某一个分支上,这个分支的父节点一定是某一个阶段的最优方案,就像分封制一样,离最后的最优方案血缘关系越远优秀程度越差,嗯我是这么理解的.....
因为动态规划每一个阶段的方案只和前一个阶段有关,所以可以在输入数据后直接j循环,不需要v[i]w[i]之类存储每一个状态的数组...
1 #include<iostream> 2 #include<cstdio> 3 #include<cstring> 4 #include<algorithm> 5 #include<cmath> 6 using namespace std; 7 int k,v,n; 8 long long f[5010][51]={}; 9 long long st1[5010]={}; 10 long long st2[5010]={}; 11 int main(){ 12 cin>>k>>v>>n; 13 for(int i=0;i<v;i++){ 14 for(int j=0;j<=k;j++){ 15 f[i][j]=-99999999; 16 } 17 } 18 f[0][1]=0; 19 for(int i=1;i<=n;i++){ 20 int a,b; 21 cin>>a>>b; 22 for(int j=v;j>=a;j--){ 23 if(f[j-a][1]>=0){ 24 int p1=1,p2=1; 25 for(int u=1;u<=k;u++){ 26 st1[u]=f[j-a][u]+b; 27 st2[u]=f[j][u]; 28 if(st2[p2]>=st1[p1]) f[j][u]=st2[p2++]; 29 else f[j][u]=st1[p1++]; 30 } 31 } 32 } 33 } 34 long long ans=0; 35 for(int i=1;i<=k;i++){ 36 ans+=f[v][i]; 37 } 38 cout<<ans<<endl; 39 return 0; 40 }