题意:给出k家店,每家店内有各种价格的鞋子(同样的鞋子只能买一双),每双鞋子有价值,要求每家店至少买一双。给出m钱,求获得的最大价值。
思路:分组背包严重变形了,变成了相反的,每组物品至少挑1件(分组背包是至多挑1件)。虽然是分组背包的变形,但是用到的却是01背包的思路。要求每家店至少买1双,那么可以只买一双双,也可以买多双。难点在这。需要维护两行dp状态值即可。第一行是前面组的物品的最佳状态,第二行是第i件物品之前的最佳状态(已经装进同组物品)。
对于i组第t件物品,(1)要么从i组之前的状态转移而来(即目前为止,i组只买第t件),(2)要么从已经买了i组第t件之前(包括1~i-1组)的状态转移而来。但是这样并不能保证第i组一定会有鞋子被选中,有可能第t件不划算,不装,同时,相比同组的其他件也不划算,不装。为解决此问题,先预装一件最便宜的上去,即上面提到的第二行更新为“必装第i组最便宜的鞋”,这样保证了至少每组至少一件。如果官方答案中应该第i组只可能装一件,且不是最便宜的那件,那么用第(1)种转移方法肯定可以覆盖掉前面预装的;如果应该第i组要装多件,那么从第(2)种转移方法就能从同组中多件一块考虑。
至关重要的是,对于每种容量j,如果买不起所有店最便宜的鞋子,那么作废,因为不符合要求。比如说,考虑第2组第1件时,dp[1][0~第1组中最便宜鞋价-1]的状态作废,因为0~第1组中最便宜鞋价-1这段状态已经连第1组中最便宜的鞋都买不起,是不符合要求的,即使答案用到这些状态,那也是不符合要求的,所以提前作废,只需要在考虑容量时注意下限就行了。
巨坑!每家店可是不一定有鞋子的,比如给出k家店,k-1件鞋子,这是可能的。一旦出现这种情况,立马输出“不可能”。
1 #include <bits/stdc++.h> 2 using namespace std; 3 int n, m, k, w, dp[15][11000]; 4 struct node 5 { 6 int p,v; 7 }r; 8 vector< vector<node> > vect; 9 inline int cmp(node a,node b){return a.p< b.p? 1: 0;} 10 int cal() 11 { 12 memset(dp,0,sizeof(dp)); 13 int up=0; 14 for(int i=1; i<=k; i++) //每组 15 { 16 r=vect[i][0]; 17 for(int j=m; j>=up+r.p; j--) //先装进去每组中最便宜的一个,有更好的再更新 18 dp[i][j]=dp[i-1][j-r.p]+r.v; 19 20 for(int t=1; t<vect[i].size(); t++)//同组每种鞋 21 { 22 r=vect[i][t]; 23 for(int j=m; j>=up+r.p; j--) //每种容量。注意下限是前面所有店的最便宜鞋价的总和。最差也能买上前面所有店的最便宜的鞋子,其他都是无效的状态。 24 { 25 dp[i][j]=max(dp[i][j], dp[i-1][j-r.p]+r.v ); //单独放。 26 dp[i][j]=max(dp[i][j], dp[i][j-r.p] +r.v ); //配合同组放。 27 } 28 } 29 up+=vect[i][0].p; //更新下限 30 } 31 if(dp[k][m]>0) return 1; 32 else return 0; 33 } 34 void init() 35 { 36 vect.clear(); 37 vector<node> tmp; 38 for(int i=0; i<=k; i++) vect.push_back(tmp); 39 } 40 41 int main() 42 { 43 freopen("input.txt", "r", stdin); 44 while(cin>>n>>m>>k) 45 { 46 init(); 47 for(int i=0; i<n; i++) 48 { 49 scanf("%d%d%d", &w, &r.p, &r.v); 50 vect[w].push_back(r);//分组保存 51 } 52 int big=0; 53 for(int i=1; i<=k; i++) 54 { 55 sort(vect[i].begin(), vect[i].end(), cmp); //排个序,最低价的排在前面 56 if(!vect[i].empty()) big+=vect[i][0].p; //坑在这,有的店完全没有鞋子! 57 else big=0x7fffffff;//既然没有鞋子,置为无穷大,表示买不起。 58 } 59 if(big>m){printf("Impossible\n");continue;} //每家店最便宜的鞋子都买不起 60 if(cal()) printf("%d\n",dp[k][m]); 61 else printf("Impossible\n"); 62 } 63 return 0; 64 }
AC代码
时间: 2024-10-11 18:28:40