Codeforces Round #691 (Div. 2) D. Glass Half Spilled

题意:
给出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);
    }
}

猜你喜欢

转载自blog.csdn.net/weixin_45755679/article/details/113409509