Codeforces Round #691 (Div. 1) B. Glass Half Spilled 背包DP
B. Glass Half Spilled
There are 𝑛 glasses on the table numbered 1,…,𝑛. The glass 𝑖 can hold up to 𝑎𝑖 units of water, and currently contains 𝑏𝑖 units of water.
You would like to choose 𝑘 glasses and collect as much water in them as possible. To that effect you can pour water from one glass to another as many times as you like. However, because of the glasses' awkward shape (and totally unrelated to your natural clumsiness), each time you try to transfer any amount of water, half of the amount is spilled on the floor.
Formally, suppose a glass 𝑖 currently contains 𝑐𝑖 units of water, and a glass 𝑗 contains 𝑐𝑗 units of water. Suppose you try to transfer 𝑥 units from glass 𝑖 to glass 𝑗 (naturally, 𝑥 can not exceed 𝑐𝑖). Then, 𝑥/2 units is spilled on the floor. After the transfer is done, the glass 𝑖 will contain 𝑐𝑖−𝑥 units, and the glass 𝑗 will contain min(𝑎𝑗,𝑐𝑗+𝑥/2) units (excess water that doesn't fit in the glass is also spilled).
Each time you transfer water, you can arbitrarlly choose from which glass 𝑖 to which glass 𝑗 to pour, and also the amount 𝑥 transferred can be any positive real number.
For each 𝑘=1,…,𝑛, determine the largest possible total amount of water that can be collected in arbitrarily chosen 𝑘 glasses after transferring water between glasses zero or more times.
Input
The first line contains a single integer 𝑛 (1≤𝑛≤100) — the number of glasses.
The following 𝑛 lines describe the glasses. The 𝑖-th of these lines contains two integers 𝑎𝑖 and 𝑏𝑖 (0≤𝑏𝑖≤𝑎𝑖≤100, 𝑎𝑖>0) — capacity, and water amount currently contained for the glass 𝑖, respectively.
Output
Print 𝑛 real numbers — the largest amount of water that can be collected in 1,…,𝑛 glasses respectively. Your answer will be accepted if each number is within 10−9 absolute or relative tolerance of the precise answer.
Example
input
3
6 5
6 5
10 2
output
7.0000000000 11.0000000000 12.0000000000
Note
In the sample case, you can act as follows:
for 𝑘=1, transfer water from the first two glasses to the third one, spilling (5+5)/2=5 units and securing 2+(5+5)/2=7 units;
for 𝑘=2, transfer water from the third glass to any of the first two, spilling 2/2=1 unit and securing 5+5+2/2=11 units;
for 𝑘=3, do nothing. All 5+5+2=12 units are secured.
题意
现在有n个水杯,每个水杯最多盛a[i]升水,现在每个水杯已经有b[i]升水
你可以操作若干次,将A杯的水倒若干升到B杯,但是注意,你倒水的时候会洒掉一半。
请问在操作若干次之后,让你选择k杯,最后最多能有多少升水。
题解
我们假设已经知道了最后选择的K杯是哪几杯。
那么我们令最初的所有杯子的水的和为S1,最后选择的K杯的水的和为S2,最后k杯水的容量为S3,那么:
别的杯子最多给这k杯倒 (S1-S2)/2
那么答案就是 min(S3, S2 + (S1-S2)/2) = min(S3,(S2+S1)/2)
其中的未知数有S2,S3;现在的问题就是需要找到S2和S3的关系。
DP[i][j][k],表示我们考虑前i个杯子,当前选了j个杯子,容量为k时,我最多能放多少水。
经典老DP问题了:
现在有一个背包,容积为k,我只能拿j个物品,每个物品只能拿一次,问你最大价值是多少。
代码
#include<bits/stdc++.h>
using namespace std;
const int maxn = 1e4+7;
int dp[105][maxn]; // 前i个我选择j个,容量为k的时候,最多能装多少
int a[105],b[105],n,suma,sumb;
int main() {
cin>>n;
for (int i=1;i<=n;i++) {
cin>>a[i]>>b[i];
suma+=a[i];
sumb+=b[i];
}
for (int i=0;i<=n;i++) {
for (int j=0;j<=10000;j++) {
dp[i][j]=-1e9;
}
}
dp[0][0]=0;
for (int i=1;i<=n;i++) {
for (int j=n;j>=1;j--) {
for (int c=suma;c>=a[i];c--) {
dp[j][c]=max(dp[j][c],dp[j-1][c-a[i]]+b[i]);
}
}
}
for (int j=1;j<=n;j++) {
double ans = 0.0;
for (int c=0;c<=suma;c++) {
// dp[j][c]+(sumb-dp[j][c])/2
ans = max(ans, min(1.0*dp[j][c]+sumb, 2.0*c));
}
printf("%.10f ", ans/2.0);
}
}