首页 > 代码库 > 背包问题总结二

背包问题总结二

上一篇总结了三种基本的背包类型,但做题时很少让直接赤裸裸的求某一种背包。由它们延伸出来的问题可能更加重要。

但只要理解了基本的三种背包,对于更加复杂的问题的理解也不是很难。

仍然参考背包九讲的内容。


混合三种背包


将三种背包混合起来,就是说有的物品只有一件,有的物品有无限件,而有的物品有n[i]件。求把物品装入背包不超过背包容量的最大价值。

听起来很高大上,其实把它们分别开来,对不同类型的问题进行相应的背包即可。。

for(int i = 1; i <= N; i++)
{
	if(第i件物品属于01背包)
		ZeroOnePack(c[i],w[i]);
	else if(第i件物品属于完全背包)
		CompletePack(c[i],w[i]);
	else if(第i件物品属于多重背包)
		MultiplePack(c[i],w[i],n[i]);
}


二维费用的背包

对于每种物品,都有两种费用,选择这件物品必须付出两种代价。对于每种代价都有一个可付出的最大值(背包容量)。问怎样选择物品可以得到最大的价值。

假设这两种代价分别是代价1和代价2,第i件物品所需的两种代价分别是a[i]和b[i],两种代价可付出的最大值(两种背包容量)分别是V和U,物品的价值为w[i]。


设f[i][v][u]表示前i种物品付出代价分别是v和u时的最大价值。那么可以得到状态方程:

f[i][v][u] = max(f[i-1][v][u],f[i-1][v-a[i]][u-b[i]]+w[i])。

同样可以以二维数组的形式f[v][u]表示,只是相对f[v]增加了一维。当是01背包时v和u采用逆序循环,当是完全背包时v和u采用顺序循环,当是多重背包时拆分物品转化为01背包解决。


特别的,经常增加的一个代价是物品总个数的限制。即最多只能取M件物品。这相当于每种物品多了一个"件数"的代价,每件物品的件数费用均为1,可以付出最大件数费用为M,即背包容量为M,f[v][m]就表示前i种物品付出费用是v,最多选m件时的最大价值,然后根据不同类型的背包问题选择逆序和顺序等解决。

例如hdu 2159,就是有件数限制的完全背包,解法:点击打开链接。



分组背包

有N件物品,和一个容量为V的背包,第i件物品的费用是c[i],价值是w[i]。把这些物品划分成若干个组,每组中的物品时相互冲突的,最多选一件。求将哪些物品装入背包可使得这些物品费用总和不超过背包容量且总价值最大。


问题和01背包有些类似,它是每组物品有两种选择,是选该组的某一件还是一件也不选。

定义子结构f[k][v]表示前k组物品花费是v能获得的最大价值。那么

f[k][v] = max(f[k-1][v],f[k-1][v-c[i]]+w[i] | 物品i属于第k组)。

伪代码为

for 所有的组k

for v = V...0

for 所有的物品i属于组k

f[v] = max(f[v],f[v-c[i]]+w[i])

注意第二层循环必须在第三层循环的外面,这样才能保证每组只选一个。否则就是未优化多重背包了。


比较明显的分组背包是hdu 1712,有n种作业,每种作业都有一定的花费(天数)和一定的价值,问m天能获得的最大价值。这里n种作业相当于把物品分了n组,每组要么选一个,要么不选,即每种作业要么选一定天数做要么不做。


还有一个比较经典的,能够很好的诠释分组背包的问题hdu 4341,黄金矿工小游戏,每个黄金有到原点的距离、时间(代价)和价值,对于在一条线上的黄金只能拿近的而不能越过近的拿远的,问再T时间内能抓到的最大价值。虽然不是明显的分组背包,但很容易看出对于一条线上的黄金,可以把它们看做一组物品,在这一组中只能取一个或不取,只能取一个怎么去实现呢?就是将这一条线上在这个黄金前面的价值和时间都累加到这个黄金上,若最后取了这一组中的这个黄金,也就相当于这个黄金以及在它上面的黄金都已经被取了,符合题意。


延伸:hdu 3033,也是分组背包,但要求是每组中至少取一个。


背包问题问法的变化

求方案数:给出n个物品的费用以及背包容量时,求将背包装满或装至一定容量的方案数。

对于这类问题,一般只需将状态转移方程的max改成sum,例如每件物品均是完全背包中的物品,转移方程为

f[i][v] = sum{f[i-1][v],f[i-1][v-c[i]]}。初始条件f[0][0] = 1。


求最优方案数:在物品总价值的条件下的方案数。增加g[i][v]表示这个子问题的最优方案数,则在求f[i][v]的同时求g[i][v]。以01背包为例:

for i = 1...N

for v=0...V

f[i][v] = max(f[i-1][v],f[i-1][v-c[i]]+w[i])

g[i][v] = 0;

if(f[i][v] == f[i-1][v])

inc(g[i][v],g[i-1][v])

if(f[i][v] == f[i-1][v-c[i]]+w[i])

inc(g[i][v],g[i-1][v-c[i]])



背包问题先总结到这,但还不够充分,以后有新的内容再往上添加。