http://acm.hdu.edu.cn/showproblem.php?pid=1712
物品分多组,每组中的每件都是冲突的,那么,背包问题变成,在某组中取一个,使得价值达到最大,或者这组一个都不取能使价值达到最大,在01背包基础上加一个循环就可以,在减去体积的时候依次减去每组每一个的体积,这里的体积正好对应纵坐标,所以不必再抽象成c[i]了,价值w[i]<-a[i][j];
问题
有N件物品和一个容量为V的背包。第i件物品的费用是c[i],价值是w[i]。这些物品被划分为若干组,每组中的物品互相冲突,最多选一件。求解将哪些物品装入背包可使这些物品的费用总和不超过背包容量,且价值总和最大。
算法
这个问题变成了每组物品有若干种策略:是选择本组的某一件,还是一件都不选。也就是说设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]}
注意这里的三层循环的顺序,甚至在本文的第一个beta版中我自己都写错了。“for v=V..0”这一层循环必须在“for 所有的i属于组k”之外。这样才能保证每一组内的物品最多只有一个会被添加到背包中。
另外,显然可以对每组内的物品应用P02中“一个简单有效的优化”。
小结
分组的背包问题将彼此互斥的若干物品称为一个组,这建立了一个很好的模型。不少背包问题的变形都可以转化为分组的背包问题(例如P07),由分组的背包问题进一步可定义“泛化物品”的概念,十分有利于解题。
1 #include<stdio.h> 2 int max(int a,int b) 3 { 4 return a>b?a:b; 5 } 6 int main() 7 { 8 int i,k,j,n,m,dp[110],a[110][110]; 9 while(scanf("%d%d",&n,&m),n||m) 10 { 11 for(i=1;i<=n;i++) 12 for(j=1;j<=m;j++) 13 scanf("%d",&a[i][j]); 14 dp[0]=0; 15 for(i=1;i<110;i++)dp[i]=0; 16 for(k=1;k<=n;k++) 17 for(j=m;j>=0;j--) 18 for(i=0;i<=j;i++) 19 dp[j]=max(dp[j],dp[j-i]+a[k][i]);//这里体积正好对应纵坐标,所以k组中的i就是相对应物品中的体积,价值是a[k][i] 20 printf("%d\n",dp[m]); 21 } 22 return 0; 23 }