题意:
给出n个水杯的容量和其已经包含的水量,有如下操作,选择两个杯子,将其中一个杯子中的x容量水转移到另一个杯子,但是会洒出x/2的水。现在求,对于1-n中每一个数字k,任意选择k个杯子,其水量之和最大是多少。
题解:
首先,对于被选的那k个杯子,它们一定不会倒水出去,所以我们先不考虑倒水,设dp[i][j][k]表示前i个水杯,选j个,其容量为k能够得到的最大水量。
那么状态转移方程就是:
dp[i][j][k]=max(dp[i-1][j][k],dp[i][j][k]);//不要第i个水杯
dp[i][j][k]=max(dp[i-1][j-1][k-a[i]]+b[i],dp[i][j][k]);//要第i个水杯
再来考虑倒水,假设总水量有sum,那么剩余的水量就是
sum-dp[i][j][k],那么可用的水量就是(sum-dp[i][j][k])/2。
那么min(dp[i][j][k]+(sum-dp[i][j][k])/2,k)就是最终可以达到的最大水量,最后再取个max。
注意:状态转移时,要用滚动数组。
代码:
#include<bits/stdc++.h>
#include<cstdio>
#include<iostream>
#include<algorithm>
#include<cstring>
#include<cmath>
#include<queue>
#include<map>
#include<stack>
#include<set>
#define iss ios::sync_with_stdio(false)
using namespace std;
typedef unsigned long long ull;
typedef long long ll;
typedef pair<int,int>pii;
const int MAXN=1e4+5;
const int mod=77797;
const int inf=0x3f3f3f;
int dp[2][105][MAXN];
int a[105],b[105];
int main()
{
int n;
cin>>n;
int sum1=0;
int sum2=0;
for(int i=1;i<=n;i++)
{
cin>>a[i]>>b[i];
sum1+=a[i];
sum2+=b[i];
}
int t=1;
memset(dp,-inf,sizeof dp);
dp[0][0][0]=0;
for(int i=1;i<=n;i++,t^=1)
{
for(int j=0;j<=i;j++)
{
for(int k=0;k<=sum1;k++)
{
dp[t][j][k]=max(dp[t][j][k],dp[t^1][j][k]);
if(j&&k>=a[i]) dp[t][j][k]=max(dp[t^1][j-1][k-a[i]]+b[i],dp[t][j][k]);
}
}
}
for(int i=1;i<=n;i++)
{
int ans=0;
for(int j=0;j<=sum1;j++)
{
ans=max(ans,min(dp[t^1][i][j]+sum2,2*j));
}
printf("%.10lf ",ans/2.0);
}
}