动态规划:状态表示(集合:f(i,j) 的含义;属性:求Max (例))+状态计算(如何进行状态转移、计算)
由于仅做复习用,具体推导未详细写出。


一:01背包问题

每个物品有且只有一个。

  1. #include <bits/stdc++.h>
  2. using namespace std;
  3. const int N=1010;
  4. int n,m;
  5. int f[N];
  6. int v[N],w[N];
  7. int main()
  8. {
  9. cin>>n>>m;
  10. for(int i=1;i<=n;i++)
  11. cin>>v[i]>>w[i];
  12. for(int i=1;i<=n;i++)
  13. {
  14. for(int j=m;j>=v[i];j--) //类似滚动数组原理,j从大到小遍历避免覆盖之前的存值
  15. {
  16. f[j]=max(f[j],f[j-v[i]]+w[i]);//上层状态,必选第i件的情况
  17. }
  18. }
  19. cout<<f[m]<<endl;
  20. return 0;
  21. }

二:完全背包

每个物品有无限个。
【唯一从小到大更新w[i]的题类】

  1. #include<bits/stdc++.h>
  2. using namespace std;
  3. const int N=1010;
  4. int n,m;
  5. int f[N],v[N],w[N];
  6. int main()
  7. {
  8. cin>>n>>m;
  9. for(int i=1;i<=n;i++)
  10. cin>>v[i]>>w[i];
  11. for(int i=1;i<=n;i++)
  12. for(int j=v[i];j<=m;j++) //与01背包解法的唯一区别,从小到大遍历,更新存值
  13. f[j]=max(f[j],f[j-v[i]]+w[i]);
  14. cout<<f[m]<<endl;
  15. return 0;
  16. }

三:多重背包问题 II

每个物品给定了数量。
对于每个物品的数量s[i],用二进制优化了查找,复杂度为O(vwlog2(s))。

  1. #include <bits/stdc++.h>
  2. using namespace std;
  3. const int N=24100; //这里的N是根据n的范围乘上log2(s)得到的
  4. int n,m;
  5. int f[N],v[N],w[N],cnt;
  6. int main()
  7. {
  8. cin>>n>>m;
  9. for(int i=1;i<=n;i++)
  10. {
  11. int a,b,s;
  12. cin>>a>>b>>s;
  13. int k=1; //得到所有组合
  14. while(k<=s) //二进制优化查找
  15. {
  16. cnt++;
  17. v[cnt]=a*k;
  18. w[cnt]=b*k;
  19. s-=k;
  20. k=k*2;
  21. }
  22. if(s>0) //最后剩余的(此时s>2^t && s<2^(t+1))
  23. {
  24. cnt++;
  25. v[cnt]=a*s;
  26. w[cnt]=b*s;
  27. }
  28. }
  29. n=cnt; //不要忘记更新n的范围!!!
  30. for(int i=1;i<=n;i++) //用01背包解法即可
  31. for(int j=m;j>=v[i];j--)
  32. f[j]=max(f[j],f[j-v[i]]+w[i]);
  33. cout<<f[m]<<endl;
  34. return 0;
  35. }

四:分组背包问题

物品有多组,每组里面有多个物品,但每组内只能选一个物品。

  1. #include <bits/stdc++.h>
  2. using namespace std;
  3. const int N=110;
  4. int n,m;
  5. int f[N],v[N][N],w[N][N],s[N];
  6. int main()
  7. {
  8. cin>>n>>m;
  9. for(int i=1;i<=n;i++)
  10. {
  11. cin>>s[i];
  12. for(int j=0;j<s[i];j++)
  13. cin>>v[i][j]>>w[i][j];
  14. }
  15. for(int i=1;i<=n;i++)
  16. for(int j=m;j>=0;j--) //从大到小,避免覆盖
  17. for(int k=0;k<s[i];k++) //遍历挑选组内物品
  18. if(v[i][k]<=j) //可装
  19. f[j]=max(f[j],f[j-v[i][k]]+w[i][k]);
  20. cout<<f[m]<<endl;
  21. return 0;
  22. }