Glass Half Spilled
题意
在编号为 1 1 1 的桌子上有 n n n 个玻璃杯,第 i i i 个杯子可以容纳最多 a i a_i ai 单位水,当前包含 b i b_i bi 单位水。
选择 k k k 个玻璃杯,并尽可能多地收集水。为了达到这个效果,你可以把水从一个玻璃杯倒到另一个玻璃杯,想倒多少次就倒多少次 然而,每次你试图转移任何数量的水时,都会有一半的水洒在地板上。对于每个 k = 1 、 2 、 3 、 … n k=1、2、3、\dots n k=1、2、3、…n,确定在零次或多次在玻璃杯之间转移水后,任意选择的k个玻璃杯可以收集的最大可能总量。
思路
设 d p ( i , j , k ) dp(i,j,k) dp(i,j,k) 为前 i i i 个杯子中选择 j j j 个杯子 杯子容量上限之和为 k k k 时的最大水量之和
转移方程为 $dp(i,j,k) = $ { d p [ i − 1 ] [ j ] [ k ] d p [ i − 1 ] [ j − 1 ] [ k − a [ i ] ] + b [ i ] \begin{cases}dp\left[ i-1\right] \left[ j\right] \left[ k\right] \\ dp\left[ i-1\right] \left[ j-1\right] \left[ k-a\left[ i\right] \right] +b\left[ i\right] \end{cases} {dp[i−1][j][k]dp[i−1][j−1][k−a[i]]+b[i]
设 s u m b sumb sumb 为初始水量之和
设 s u m a suma suma 为杯子的容量之和
假设求出最大水量之和为 d p ( i , j , k ) dp(i,j,k) dp(i,j,k) 则转移的水量为 s u m b − d p ( i , j , k ) 2 \dfrac{sumb - dp(i,j,k)}{2} 2sumb−dp(i,j,k)
最终的水量为 m i n min min ( d p ( i , j , k ) + s u m b − d p ( i , j , k ) 2 , k ) (dp(i,j,k) + \dfrac{sumb - dp(i,j,k)}{2}, k) (dp(i,j,k)+2sumb−dp(i,j,k),k) 同时乘以 2 2 2 并化简得
r e s = m i n ( d p ( i , j , k ) + s u m b , 2 ∗ k ) res = min(dp(i,j,k) + sumb, 2 * k) res=min(dp(i,j,k)+sumb,2∗k)
输出时记得 r e s / 2 res / 2 res/2
代码
#include<bits/stdc++.h>
#define INF 0x3f3f3f3f
#define mod 1000000007
using namespace std;
typedef long long LL;
typedef pair<int, int>PII;
inline LL gcd(LL a, LL b) { return b ? gcd(b, a % b) : a; }
const int N = 110;
int n;
int a[N], b[N];
double dp[2][N][N * N];
void solve() {
cin >> n;
int suma = 0, sumb = 0;
for (int i = 1; i <= n; ++i) {
scanf("%d %d", &a[i], &b[i]);
suma += a[i];
sumb += b[i];
}
//预处理
for (int j = 0; j <= n; ++j)
for (int k = 0; k <= suma; ++k)
dp[0][j][k] = -INF;
dp[0][0][0] = 0;
//
int t = 1;//滚动数组
for (int i = 1; i <= n; ++i,t ^= 1) {
for (int j = 0; j <= n; ++j) //不选第i个杯子
for (int k = 0; k <= suma; ++k)
dp[t][j][k] = dp[t ^ 1][j][k];
for (int j = 1; j <= n; ++j) //选第i个杯子
for (int k = a[i]; k <= suma; ++k)
dp[t][j][k] = max(dp[t][j][k], dp[t ^ 1][j - 1][k - a[i]] + b[i]);
}
for (int j = 1; j <= n; ++j) {
double res = 0;
for (int k = 0; k <= suma; ++k)
res = max(res, min(double (2.0 * k), dp[t ^ 1][j][k] + sumb));
printf("%.10lf ", res / 2);
}
cout << endl;
}
int main() {
//int t; cin >> t;
//while (t--)
solve();
return 0;
}