01背包
什么是01背包?
- 01背包就是在M件物品取出若干件放在空间为W的背包里,每件物品的体积为C1,C2,…,Cn,与之相对应的价值为W1,W2,…,Wn。求将哪些物品装入背包可使总价值最大。
01背包的分析:
- 子问题:F[i][j]表示前 i 件物品中选取若干件物品放入剩余空间为 j 的背包中所能得到的最大价值。
- 判断第 i 件物品放还是不放:
- F [ i ][ j ] = max { F [ i-1 ][ j ] ,F[ i-1 ][ j-C[ i ] ] + w[ i ] }。
- F [ i-1 ][ j ] 表示前i-1件物品中选取若干件物品放入剩余空间为j的背包中所能得到的最大价值。
- F[ i-1 ][ j-C[ i ] ] + w[ i ] 表示前i-1件物品中选取若干件物品放入剩余空间为j-C[i]的背包中所能取得的最大价值加上第i件物品的价值。
模板代码:
- 二维数组:
#include <iostream> #include <string.h> #include <stdio.h> int dp[1001][1001],c[1001],w[1001]; int main() { int t,k,v,i,j; scanf("%d",&t); while(t--) { scanf("%d %d",&k,&v); memset(dp,0,sizeof(dp));//初始化操作 for(i=1;i<=k;i++) scanf("%d",&w[i]); for(i=1;i<=k;i++) scanf("%d",&c[i]); for(i=1;i<=k;i++) { for(j=0;j<=v;j++) { if(c[i]<=j)//表示第i个物品将放入大小为j的背包中 dp[i][j]=dp[i-1][j]>(dp[i-1][j-c[i]]+w[i])?dp[i-1][j]:(dp[i-1][j-c[i]]+w[i]);//第i个物品放入后,那么前i-1个物品可能会放入也可能因为剩余空间不够无法放入 else dp[i][j]=dp[i-1][j];//第i个物品无法放入 } } printf("%d\n",dp[k][v]); } return 0; }
- 一维数组:(优化版)
#include <iostream> #include <string.h> #include <stdio.h> using namespace std; int dp[1001],c[1001],w[1001]; int main() { int t,k,v,i,j; scanf("%d",&t); while(t--) { scanf("%d %d",&k,&v); memset(dp,0,sizeof(dp)); for(i=1;i<=k;i++) scanf("%d",&w[i]); for(i=1;i<=k;i++) scanf("%d",&c[i]); for(i=1;i<=k;i++) { for(j=v;j>=c[i];j--) { dp[j]=dp[j]>(dp[j-c[i]]+w[i])?dp[j]:(dp[j-c[i]]+w[i]); } } printf("%d\n",dp[v]); } return 0;
- 该方法只是对背包的空间进行优化。
- 事实上,这要求在每次主循环中我们以v=V..0的顺序推dp[v],这样才能保证推dp[v]时dp[v-c[i]]保存的是状态dp[i-1][v-c[i]]的值。
- 所以这种解法只能由V--0,不能反过来,如果反过来就会造成物品重复放置!