01背包
转载于:点击打开链接
问题描述:
01背包(ZeroOnePack):有N件物品和一个容量为V的背包,每种物品均只有一件。且每i件物品的重量为weight[i],价值为value[i]。求解将哪些物品装入背包可使这些物品的重量总和不超过背包容量,且价值总和最大。
背包特点:
每种物品仅有一件,可以选择放或不放。
基本思路:
用子问题定义状态:即DP[i][v]表示前i件物品恰好放入容量为v的背包可以获得的最大价值。则其状态转移方程是:
对于以上的方程必须进行详细的解释下:“将前i件物品放入容量为v的背包中”这个子问题,若只考虑第i件物品的策略(放或不放),那么就可以转化为一个只牵扯前i-1件物品的问题。
第一种情况:如果第i件不放进去,那么问题就转化为“前i-1件物品放入容量为v的背包中”,价值为DP[i-1][v];
第二种情况:如果放第i件物品,那么问题就转化为“前i-1件物品放入剩下的容量为v-weight[i]的背包中”,此时能获得的最大价值就是DP[i-1][v-weight[i]]再加上通过放入第i件物品获得的价值value[i],及DP[i-1][v-weight[i]]+value[i];
最后再比较第一种与第二种所得的价值大小,哪种价值大,DP[i][v]的值就是哪种。
优化空间复杂度:
以上的时间和空间的复杂度均为O(VN),其中时间复杂度应该已经不能在优化了,但时间复杂度却可以优化到O(V);
我们用一维数组来存储DP值,及用DP[0….v]表示:DP[v]表示把前i件物品放入容量为v的背包里得到的价值。
首先要知道,我们是通过i从1到n的循环来依次表示前i件物品存入的状态。即:for i=1..N
现在思考如何能在是DP[v]表示当前状态是容量为v的背包所得价值,而又使DP[v]和DP[v-weight[i]]+value[i]标签前一状态的价值?
这里我们采用了逆序保存DP[v]的值,这要求在每次主循环中我们以v=V..0的顺序推DP[v],这样才能保证推DP[v]时DP[v-weight[i]]保存的是状态DP[i-1][v-weight[i]]的值(及前一个状态的值)。
伪代码如下:
分析上面的代码:当内循环是逆序时,就可以保证后一个DP[v]和DP[v-weight[i]]+value[i]是前一状态的!
代码实现:
初始化的细节问题:
有的问题中有要求“恰好装满背包”,有的则没有要求,这就在初始化DP数组时有所不同。
恰好装满背包:则初始化时,DP[0]=0, 其他的DP[1…..V]均设为负的无穷大。
无须恰好装满背包:则初始化时,DP[1…..V]全部设为0;
一个常数优化:
前面的伪代码中有for v=V…..0,可以将这个循环下限进行改进。由于只需要最后DP[V]的值,倒推前一个物品,其实只要知道DP[v-weight[n]]即可。以此类推,对以第j个背包,其实只需要知道到DP[v-sum{weight[j….n]}]即可,即代码如下:
sum[n]=weight[n];
例题:
http://acm.hdu.edu.cn/showproblem.php?pid=2602
http://acm.hdu.edu.cn/showproblem.php?pid=1203
http://acm.hdu.edu.cn/showproblem.php?pid=2955
http://acm.hdu.edu.cn/showproblem.php?pid=3496
完全背包
问题描述:
完全背包(CompletePack):有N件物品和一个容量为V的背包,每种物品都有无限可用。且每i件物品的重量为weight[i],价值为value[i]。求解将哪些物品装入背包可使这些物品的重量总和不超过背包容量,且价值总和最大。
背包特点:
每种物品有无限多件。也就是从每种物品的角度考虑,与它相关的策略已并非取或不取两种,而是有取0件、取1件、取2件……等很多种。
基本思路:
如果仍然按照解01背包时的思路,令DP[i][v]表示前i种物品恰好放入一个容量为v的背包的最大权值,仍然可以按照每种物品不同的策略写出状态方程:
优化空间复杂度:
同样我们也可以用一维数组来存储DP值,及用DP[0….v]表示。
伪代码如下:
分析上面的代码:
想必大家看出了和01背包的区别,这里的内循环是顺序的,而01背包是逆序的。
现在关键的是考虑:为何完全背包可以这么写?
再次我们先来回忆下,01背包逆序的原因?是为了是max中的两项是前一状态值,这就对了。那么这里,我们顺序写,这里的max中的两项当然就是当前状态的值了,为何?
因为每种背包都是无限的。当我们把i从1到N循环时,DP[v]表示容量为v在前i种背包时所得的价值,这里我们要添加的不是前一个背包,而是当前背包。所以我们要考虑的当然是当前状态。
代码实现:
例题:
http://acm.hdu.edu.cn/showproblem.php?pid=1114
http://acm.hdu.edu.cn/showproblem.php?pid=1248
多重背包
问题描述:
多重背包(MultiplePack):有N件物品和一个容量为V的背包,第i种物品最多有bag[i]件可用。且每i件物品的重量为weight[i],价值为value[i]。求解将哪些物品装入背包可使这些物品的重量总和不超过背包容量,且价值总和最大。
背包特点:
这题目和完全背包问题很类似。基本的方程只需将完全背包问题的方程略微一改即可,因为对于第i种物品有bag[i]+1种策略:取0件,取1件……取bag[i]件。
基本思路:
令DP[i][v]表示前i种物品恰好放入一个容量为v的背包的最大权值,则其状态方程:
代码实现:
{
}
void ZeroOnePack(int v,int w)
{
}
void MultiplePack(int v,int w,int b)
{
}
for(int i=1;i<=N;i++)
或:
for(i=1;i<=nKind;i++)
}
例题:
http://acm.hdu.edu.cn/showproblem.php?pid=2191
http://acm.hdu.edu.cn/showproblem.php?pid=1171