Sicily 1346. 金明的预算方案
Description
金明今天很开心,家里购置的新房就要领钥匙了,新房里有一间金明自己专用的很宽敞的房间。更让他高兴的是,妈妈昨天对他说:“你的房间需要购买哪些物品,怎么布置,你说了算,只要不超过N元钱就行”。今天一早,金明就开始做预算了,他把想买的物品分为两类:主件与附件,附件是从属于某个主件的,下表就是一些主件与附件的例子:
如果要买归类为附件的物品,必须先买该附件所属的主件。每个主件可以有0个、1个或2个附件。附件不再有从属于自己的附件。金明想买的东西很多,肯定会超过妈妈限定的N元。于是,他把每件物品规定了一个重要度,分为5等:用整数1~5表示,第5等最重要。他还从因特网上查到了每件物品的价格(都是10元的整数倍)。他希望在不超过N元(可以等于N元)的前提下,使每件物品的价格与重要度的乘积的总和最大。 设第j件物品的价格为v[j],重要度为w[j],共选中了k件物品,编号依次为j1,j2,……,jk,则所求的总和为: v[j1]*w[j1]+v[j2]*w[j2]+ …+v[jk]*w[jk]。(其中*为乘号) 请你帮助金明设计一个满足要求的购物单。
Input
输入包含多个测试数据。 每个测试数据的第1行,为两个正整数,用一个空格隔开: N m (其中N(<32000)表示总钱数,m(<60)为希望购买物品的个数。) 从第2行到第m+1行,第j行给出了编号为j-1的物品的基本数据,每行有3个非负整数 v p q (其中v表示该物品的价格(v<10000),p表示该物品的重要度(1~5),q表示该物品是主件还是附件。如果q=0,表示该物品为主件,如果q>0,表示该物品为附件,q是所属主件的编号)
Output
对于每个测试数据,输出一个正整数,为不超过总钱数的物品的价格与重要度乘积的总和的最大值(<200000)。
Sample Input
Copy sample input to clipboard
1000 5
800 2 0
400 5 1
300 5 1
400 3 0
500 2 0
1000 5
800 2 0
400 5 1
300 5 1
400 3 0
500 2 0
Sample Output
2200 2200
【思路】
有依赖的背包问题,我的代码只是针对附件个数为2的情况,不具扩展性,更一般的情况应该是可以处理附件个数比较多的情况,不过看了别人的代码理解不了,看来还没到那个level。就先用这种过了再说吧。 我的解法是,既然附件个数不会超过两个,那么只有4种情况:主件、主件+附件1、主件+附件2、主件+附件1+附件2,把这四种情况都求出来,就变成分组背包问题,即每个组里面有4个物品(如果该主件没有附件或只有一个附件,那么这四种情况中有些不存在就记为0),然后就用分组背包的处理方法求解。
|
1 // Problem#: 1346 2 // Submission#: 1457193 3 // The source code is licensed under Creative Commons Attribution-NonCommercial-ShareAlike 3.0 Unported License 4 // URI: http://creativecommons.org/licenses/by-nc-sa/3.0/ 5 // All Copyright reserved by Informatic Lab of Sun Yat-sen University 6 #include<iostream> 7 #include<memory.h> 8 using namespace std; 9 int main() 10 { 11 int n,m; 12 while(cin>>n>>m) 13 { 14 int vv[61],pp[61],qq[61]; 15 memset(vv,0,sizeof(vv)); 16 memset(pp,0,sizeof(pp)); 17 memset(qq,0,sizeof(qq)); 18 19 20 int p[61][4],v[61][4]; 21 int a[61][2]; //一个主件最多有两个附件,没有的话就记为0 22 memset(v,0,sizeof(v)); 23 memset(p,0,sizeof(p)); 24 memset(a,0,sizeof(a)); 25 26 for(int i=1;i<=m;i++) 27 { 28 cin>>vv[i]>>pp[i]>>qq[i]; 29 if(qq[i]) //标记附件 30 { 31 if(!a[qq[i]][0]) 32 a[qq[i]][0] = i; 33 else 34 a[qq[i]][1] = i; 35 } 36 } 37 int f[61][4]; //存放每种情况的价值v*p; 38 memset(f,0,sizeof(0)); 39 for(int i=1;i<=m;i++) //变成分组背包 40 { 41 if(!qq[i]) 42 { 43 f[i][0] = vv[i] * pp[i]; 44 f[i][1] = vv[ a[i][0] ]*pp[ a[i][0] ] + vv[i] * pp[i]; 45 f[i][2] = vv[ a[i][1] ]*pp[ a[i][1] ] + vv[i] * pp[i]; 46 f[i][3] = vv[ a[i][0] ]*pp[ a[i][0] ] + vv[ a[i][1] ]*pp[ a[i][1] ] + vv[i] * pp[i]; 47 48 v[i][0] = vv[i]; 49 v[i][1] = vv[ a[i][0] ] + vv[i]; 50 v[i][2] = vv[ a[i][1] ] + vv[i]; 51 v[i][3] = vv[ a[i][0] ] + vv[ a[i][1] ] + vv[i]; 52 } 53 } 54 55 56 int dp[n+1]; 57 memset(dp,0,sizeof(dp)); 58 for(int i=1;i<=m;i++) //分组背包 59 { 60 if(!qq[i]) 61 for(int j=n;j>=0;j--) 62 for(int k=0;k<4;k++) 63 { 64 if(j-v[i][k]>=0) 65 dp[j]=max(dp[j],dp[j-v[i][k]] + f[i][k]); 66 } 67 } 68 cout<<dp[n]<<endl; 69 } 70 return 0; 71 }