赛时看了好久,思路一半对了但是没做出来,队友切了,现在赛后补题。
题意:
有$n(le 100)$个瓶子,每个瓶子有$a(le 100)$容量和$b(le 100)$的水,每次可以转移任意体积的水,但是转移的那部分水会少掉一半,同时如果瓶子满了,继续倒水就会浪费掉那部分。
现在要保留$k$个瓶子,其他扔掉。
求对于每个$k∈[1,n]$求最多剩下多少水。
思路:
考虑如果保留$k$个瓶子,瓶子集合为$S$,怎么倒水合理。
显然是其他的瓶子补满这部分瓶子的水就行了。
$ans = min{sum_{S_a}, sum_b / 2+ sum_{S_b} / 2}$
其中$sum_b / 2$为常数
然后所有的$ans$取个max就是答案了
显然对于一个确定$sum_{S_a}$,$sum_{S_b}$越大越好
我们可以用背包,求出取$k$个瓶子,容量和为$A$的水量最大值
$f[i][k][A] = max{f[i - 1][k - 1][A - a[i]] + b[i]}$
可以利用01背包的方法滚掉第一维
注意$k$和$A$的枚举要倒序
1 #include <bits/stdc++.h> 2 #define int long long 3 #define Mid ((l + r) >> 1) 4 #define lson (rt << 1) 5 #define rson (rt << 1 | 1) 6 using namespace std; 7 int read(){ 8 char c; int num, f = 1; 9 while(c = getchar(),!isdigit(c)) if(c == '-') f = -1; num = c - '0'; 10 while(c = getchar(), isdigit(c)) num = num * 10 + c - '0';; 11 return f * num; 12 } 13 int n, sum, sumb; 14 int a[109], b[109], ans; 15 int f[109][109 * 109]; 16 main() 17 { 18 n = read(); 19 memset(f, -1, sizeof(f)); 20 f[0][0] = 0; 21 for(int i = 1; i <= n; i++) sum += (a[i] = read()), sumb += (b[i] = read()); 22 for(int i = 1; i <= n; i++) { 23 for(int j = n; j; j--) { 24 for(int A = sum; A - a[i] >= 0; A--) if(f[j - 1][A - a[i]] != -1){ 25 f[j][A] = max(f[j][A], f[j - 1][A - a[i]] + b[i]); 26 } 27 } 28 } 29 double ans = 0; 30 for(int i = 1; i <= n; i++) { 31 ans = 0; 32 for(int A = 0; A <= sum; A++) if(f[i][A] != -1) { 33 ans = max(ans, min(f[i][A] / 2.0 + sumb / 2.0, A * 1.0)); 34 } 35 printf("%lf ", ans); 36 } 37 return 0; 38 }