背包问题作为一类非常具有代表性的动态规划问题,值得单独学习。
背包问题可以抽象为一定花费下的最优解,具体来说就是你有一个体积为v的背包,现在有很多的物体(肯定装不完),具有不同的价值和不同的体积,要求得背包内最大的价值是多少。
之前接触了几类背包
- 01背包
- 完全背包
- 多重背包
- 二维费用背包
01背包很具有代表性,其特点是物品都只有一个,要么不选要么选,即是0或1。
完全背包特点是物品的数量有无限个。
哎呀,很烦。
写题解好麻烦啊。
hdu-2602 Bone Collector(01背包)
这题就是01背包裸题
常规二维数组定义状态:
#include<bits/stdc++.h> using namespace std; int c[1005]; int w[1005]; int f[1005][1005]; int main() { int t; cin>>t; while(t--) { int n,v; cin>>n>>v; for(int i=1;i<=n;i++) { cin>>w[i]; } for(int i=1;i<=n;i++) { cin>>c[i]; } memset(f,0,sizeof(f)); for(int i=1;i<=n;i++) { for(int j=0;j<=v;j++) { if(j<c[i]) f[i][j]=f[i-1][j]; else f[i][j]=max(f[i-1][j],f[i-1][j-c[i]]+w[i]); } } printf("%d\n",f[n][v]); } }
一维数组优化,这个真的好巧妙,巧妙在更新状态的双重循环内部循环是从背包体积v循环到物体体积c[i],这样可以保证正常利用子问题更新,并且可以实现01背包的只使用一次。
01背包一维数组优化
#include<bits/stdc++.h> using namespace std; int value[1005]; int c[1005]; int f[1005]; int main() { int t; cin>>t; while(t--) { memset(f,0,sizeof(f)); int n,v; cin>>n>>v; for(int i=1;i<=n;i++) { cin>>value[i]; } for(int i=1;i<=n;i++) { cin>>c[i]; } for(int i=1;i<=n;i++) { for(int j=v;j>=c[i];j--) { f[j]=max(f[j],f[j-c[i]]+value[i]); } } printf("%d\n",f[v]); } }
完全背包可以通过暴力转化成01背包,毕竟背包的体积是已知的且有限的。这样就可以确定每种物品最多的数量,然后将之视为01背包。可是这样处理显然空间复杂度太高,有了之前的01背包的一维数组优化的思路,完全背包也可以用一维数组优化空间复杂度。
思路与01背包完全相同,从小到大的更新状态,不过内部循环要改为从小到大来更新,原因就在于状态转移方程,如果从小到大更新就相当于完全背包了。
hdu-1248 寒冰王座 (完全背包裸题) hdu-1248-寒冰王座
// Problem: 寒冰王座 // Contest: HDOJ // URL: http://acm.hdu.edu.cn/showproblem.php?pid=1248 // Memory Limit: 65 MB // Time Limit: 2000 ms // // Powered by CP Editor (https://cpeditor.org) #include <bits/stdc++.h> using namespace std; int a[3] = {150, 200, 350}; int f[10005]; int main() { int t; cin >> t; while (t--) { memset(f, 0, sizeof(f)); int n; // n是完全背包的体积 cin >> n; for (int i = 0; i < 3; i++) { for (int j = a[i]; j <= n; j++) { f[j] = max(f[j], f[j - a[i]] + a[i]); } } printf("%d\n", n - f[n]); } }
背包中还有一类完全装满的问题,这个只要与普通背包只是初始化不同。
hdu-1114 piggy-bank hdu-1114 piggy bank
比较难受的是,这题我一直在wa。等我做出来再更新题解吧