标签:
分组的背包问题:有N件物品和一个容量为V的背包。第i件物品的费用是c[i],价值是w[i]。这些物品被划分为若干组,每组中的物品互相冲突,最多选一件。求解将哪些物品装入背包可使这些物品的费用总和不超过背包容量,且价值总和最大。
HDU 1712:AC男有m天时间可以上课,有n种课程可以选,但是每一种课程有各种的上课时长可选(因为并不是花的时间与收获就呈线性增长的,两者之间没什么对应关系,也许花1天收获2点经验,但是花2天就不是4点经验了,可能是3点而已)。耗时与收获以矩阵的方式给出。(把同一课程的不同授课时间看成是同一组背包的不同物品,物品件没联系)。
此题与分组的背包问题几乎一模一样,还帮你把同一组内的物品给排好序了,那么就可以稍微减少一点点计算时间了。
思路:类似于01背包,那就要转成01背包的思想。第1层循环是前 i “组”,第2层循环是当前背包容量(即m)为j,第3层循环是每种授课方式。说点容易懂的话,每组物品中只能选1件,那么有多少组就是多少件了,当然也可以不选,那么问题就是在第i组中装还是不装,若装,要装哪一件?
看伪码:
for 所有的组k //组
for v=V..0 //当前背包容量
for 所有的i属于组k //组内物品
f[v]=max{f[v],f[v-c[i]]+w[i]} //时刻要保证v-c[i]>=0,总不能让数组的下标为负数吧?
第3层循环都是在同一当前背包容量下在试探到底装哪一件比较合适。假如要试探第1组,当前背包容量为10,现在开始装第1组内的第1件物品,max(0,此物品的价值),那肯定是装进去更大啦。那就装了,第3个for再次循环,现在试探装不装第1组内的第2件物品,max(0,此物品的价值)。因为第1件已经装入,那么现在在做的工作就是“试试第2、3、4...件装进去是不是更合适”。在更新的都是dp[v]这一个值,当第2个for再次循环才是更新另一个dp值。
那么假如现在第1个for是k=7,即第7组,那么第2个for的v=10,假设背包容量10为上限,第3个for开始试探第1件,发现这一件不适合装入,即装入后收获更低了。开始试探第7组的第2件了。其实这时在做的依然是“第i组的物品该不该装入背包”。
由于类似于01背包,所以第2层循环要逆序的,即从大到小,第3层最好就是顺序的了,一旦判断出第5件已经装不下,那第6件就不用再试了。这是按照HDU1712解释的。
以下有几种AC的代码:
1、容易理解 78MS
1 #include <iostream> 2 #define num 101 3 using namespace std; 4 int a[num][num]; 5 int dp[num]; 6 int max(int a,int b) 7 { 8 return a>b?a:b; 9 } 10 void cal(int n,int m) 11 { 12 int i,j,t; 13 for(i=0;i<n;i++) //组 14 for(j=m;j>0;j--) //当前背包容量 15 for(t=1;t<=j;t++) //同一组内的所有物品,刚好每组m个 16 dp[j]=max( dp[j] , dp[j-t]+a[i][t-1] ); 17 } 18 void main() 19 { 20 int n,m,i,j; 21 while(scanf("%d%d",&n,&m),n!=0) //n门课,m天 22 { 23 for(i=0;i<n;i++) 24 for(j=0;j<m;j++) 25 scanf("%d",&a[i][j]); 26 cal(n,m); 27 printf("%d\n",dp[m]); 28 memset(a,0,sizeof(int)*(num*n)); //清内存。如果怕麻烦,可以全清 29 memset(dp,0,sizeof(int)*(m+1)); //清内存 30 } 31 }
2、减少max函数 62MS
1 #include <iostream> 2 #define num 101 3 using namespace std; 4 int a[num][num]; 5 int dp[num]; 6 void cal(int n,int m) 7 { 8 int i,j,t; 9 for(i=0;i<n;i++) //组 10 for(j=m;j>0;j--) //当前背包容量 11 for(t=1;t<=j;t++) //同一组内的所有物品,刚好每组m个 12 { 13 if(dp[j]<dp[j-t]+a[i][t-1]) 14 dp[j]=dp[j-t]+a[i][t-1]; 15 } 16 } 17 void main() 18 { 19 int n,m,i,j; 20 while(scanf("%d%d",&n,&m),n!=0) //n门课,m天 21 { 22 for(i=0;i<n;i++) 23 for(j=0;j<m;j++) 24 scanf("%d",&a[i][j]); 25 cal(n,m); 26 printf("%d\n",dp[m]); 27 memset(a,0,sizeof(int)*(num*n)); //清内存。如果怕麻烦,可以全清 28 memset(dp,0,sizeof(int)*(m+1)); //清内存 29 } 30 }
3、用vector容器 62MS
1 #include <iostream> 2 #include <vector> 3 #define num 101 4 using namespace std; 5 vector<int> vect[num]; 6 int dp[num]; 7 void cal(int n,int m) 8 { 9 int i,j,t; 10 for(i=0;i<n;i++) //组 11 for(j=m;j>0;j--) //当前背包容量 12 for(t=1;t<=j;t++) //同一组内的所有物品,刚好每组m个 13 { 14 if(dp[j]<dp[j-t]+vect[i][t-1]) 15 dp[j]=dp[j-t]+vect[i][t-1]; 16 } 17 } 18 void main() 19 { 20 int n,m,i,j,temp; 21 while(scanf("%d%d",&n,&m),n!=0) //n门课,m天 22 { 23 for(i=0;i<n;i++) 24 for(j=0;j<m;j++) 25 { 26 scanf("%d",&temp); 27 vect[i].push_back(temp); 28 } 29 cal(n,m); 30 printf("%d\n",dp[m]); 31 32 for(i=0;i<n;i++) //清内存 33 vect[i].clear(); 34 memset(dp,0,sizeof(int)*(m+1)); //清内存 35 } 36 }
4、别人的代码!124MS
1 #include<iostream> 2 const int MAXN=110; 3 using namespace std; 4 int dp[MAXN]; 5 int map[MAXN][MAXN]; 6 7 int main(){ 8 int n,m; 9 while(~scanf("%d%d",&n,&m)){ 10 if(n==0&&m==0)break; 11 for(int i=1;i<=n;i++){ 12 for(int j=1;j<=m;j++){ 13 scanf("%d",&map[i][j]); 14 } 15 } 16 memset(dp,0,sizeof(dp)); 17 for(int i=1;i<=n;i++){ 18 for(int j=m;j>=1;j--){ 19 for(int k=1;k<=m;k++){ 20 if(j-k>=0){ 21 dp[j]=max(dp[j],dp[j-k]+map[i][k]); 22 } 23 } 24 } 25 } 26 printf("%d\n",dp[m]); 27 } 28 return 0; 29 30 }
HDU 1712 ACboy needs your help AC男需要你的帮助 (AC代码)分组的背包问题
标签:
原文地址:http://www.cnblogs.com/xcw0754/p/4236269.html