什么是0/1背包?
你有一个容量为V的背包,现在有N件物品,每件物品的体积为Vi,价值为Wi,求最大可装入的价值为多少?(这个问题还需要细分,有些题目会要求必须装满背包,有些则不要求将背包装满,两者的区别将会体现在给背包赋初值的情况有所不同,此处不再赘述,详情见下文)。
0/1背包的解题思路
因为0/1背包隶属于动态规划,所以必然要求其转移方程,而转移方程需要定义子状态并确定最后一步的操作。
定义子状态
我们现在有背包容量和装入物品数量这两个限制,因此我们可以使用二维数组来存储子状态,我们命名二维数组为dp[int][int],两个维度分别存放当前背包的容量和放入物品的件数,这样我们就可以知道最后一个状态必然是dp[V][N],翻译一下就是容量为V的背包装入前N个物品时的价值。
最后一步
在得到最大价值的最后一步必然是将最后一件物品装入背包容量为V的背包中,那么意味着在装入最后一件物品之前需要一个体积为(Vmax-V最后一件)的背包来存放之前的物品,此时的物品件数也需要-1,也就是说我需要寻找的子状态是dp[Vmax-V最后一件][N-1],这是把这个物品装入背包的情况。也就是0/1背包中“1”的情况。
同时也有另一种情况,就是当我为了装入最后一件物品为放弃前面的一些物品时,我的价值不升反降,这种情况下还不如不装,这便是“0”的情况。
综上所述
0/1背包就是考虑当背包容量为V时,放入物品i究竟值不值得。
倘若放入,那么当前状态dp[V][i]=dp[V-V~当前物品体积~][i-1~(因为要寻找上一个状态,上一个状态的时候物品数量要减去1)~]+W~i~(因为把当前物品放入了背包中,所以要加上当前物品的价值)
。
倘若不放入,那么当前状态等同于上一种状态,即背包容量不变,物品数量-1即dp[V][i]=dp[V][i-1];
最后判断那个价值高就选择哪种情况,因此,我们可以写出状态转移方程dp[V][i]=max(dp[V][i-1],dp[V-V~i~][i-1]);
程序主体
读入输出按下不表
memset(dp,0,sizeof(dp));//最先开始全部赋值为0
for(int i=1;i<=N;i++){//背包中物品的数量
for(int j=0;j<=V;j++){//背包容量
if(j<bone[i].vol){
dp[i][j]=dp[i-1][j];
}else{
int a=dp[i-1][j];
int b=dp[i-1][j-bone[i].vol]+bone[i].val;
dp[i][j]=a>b?a:b;
}
}
}