Gym 101081K Pope's work
题目链接:Gym - 101081K
题意:给n个箱子,每个箱子有一个重量W和一个承重R,表示它上面能放最多R-W的重量。问最多能把多少箱子堆到一堆。
思路:发现在一堆箱子里,两个箱子交换位置,对其他所有箱子没有影响。
所以我们先构造偏序关系,考虑两个箱子i和j,假设Ri<Rj。
那么我们发现假如Ri >= Wi + Wj,则Rj >= Wi + Wj。换言之,假如i上面可以放j,则j上面一定可以放j。但反之不一定成立。
所以可以认为i<=j,按照这种偏序关系排序。
按这种关系排序的好处在于,对于某个排在i后面的箱子j,假如我们想把j放在i上面,则这种情况显然不如把i放在j上面的情况好。
换言之,对于每个箱子,我们只需要考虑排在它前面的箱子即可。
接下来,用d[i][j]表示用前i个箱子堆j个箱子的最小重量。d[i][j]为INF时表示用前i个箱子不能堆j个箱子。
那么可以得到转移方程。
最终答案为使d[n][i]不为INF的最大的i。
代码如下:
1 #include"cstdio" 2 #include"iostream" 3 #include"cstring" 4 #include"algorithm" 5 #include"cstdlib" 6 #include"vector" 7 #include"set" 8 #include"map" 9 #include"cmath" 10 using namespace std; 11 typedef long long LL; 12 const LL MAXN=1010; 13 const LL MOD=1000000000+7; 14 const LL INF=0x3f3f3f3f; 15 16 struct Box 17 { 18 int w,r; 19 bool operator < (const Box x) 20 { 21 return r<x.r; 22 } 23 }; 24 Box s[MAXN]; 25 int d[MAXN][MAXN]; 26 int main() 27 { 28 #ifdef LOCAL 29 freopen("in.txt","r",stdin); 30 // freopen("out.txt","w",stdout); 31 #endif 32 int t; 33 scanf("%d",&t); 34 for(int tt=1;tt<=t;tt++) 35 { 36 int n; 37 scanf("%d",&n); 38 for(int i=1;i<=n;i++) 39 scanf("%d%d",&s[i].w,&s[i].r); 40 sort(s+1,s+1+n); 41 memset(d,INF,sizeof(d)); 42 for(int i=0;i<=n;i++) 43 d[i][0]=0; 44 for(int i=1;i<=n;i++) 45 for(int j=1;j<=i;j++) 46 { 47 d[i][j]=d[i-1][j]; 48 if(d[i-1][j-1]+s[i].w <= s[i].r && d[i-1][j-1]+s[i].w < d[i][j]) 49 d[i][j]=d[i-1][j-1]+s[i].w; 50 } 51 for(int i=n;i>=0;i--) 52 if(d[n][i]<INF) 53 { 54 printf("%d\n",i); 55 break; 56 } 57 } 58 return 0; 59 }