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

题目

补下因实验漏掉的CF(还以为是晚上,没想到是下午开始)。前三题过的很顺利,到D题时想了会发现数据很小爆搜貌似能过,就以为是道水题,交了一发T了,胡乱加了点剪枝还是T。逐渐意识到事情的严重性。考虑DP,设 \(dp[i][t][p]\)为在前 \(i\)个玻璃杯中选择 \(t\)个玻璃杯时容量为 \(p\)的所能获得的最大水量, 转移方程是 \(dp[i][t][p]=max(dp[i-1][t][p]+b[i]/2.0, dp[i-1][t-1][p-a[i]]+b[i])\),最后输出时取 \(max(min(p,dp[n][k][p]))\),本以为能过,交了一发 \(MLE\),当场懵逼。算了下空间发现大概用了 \(2e8\)\(int\),而空间限制大约是 \(1.3e8\)\(int\)那样,原因是我的 \(dp\)数组是 \(double\)类型的,太坑了。。。想办法把数组变成 \(int\)类型,因为输入都是整数,所以这是可以办到的。改写下答案表达式,设 \(sum_b\)是全部水杯里的水量之和,因为 \(dp\)数组不能储存小数了,那么让 \(dp\)数组表示在前 \(i\)个玻璃杯中选择 \(t\)个玻璃杯时容量为 \(p\)的所能获得的选择的水杯的水量之和的最大值,那么最终答案就是 \(max(min(p,dp[n][k][p]+(sum\_b-dp[n][k][p])/2.0))\),即 \(max(min(p,sum\_b/2.0+dp[n][k][p]/2.0))\)

#include<cstdio>
#include<algorithm>
#include<cmath>
using namespace std;
typedef long long ll;
const int N = 105;

int n, m;
int sum, sum_b;
int dp[N][N][N * N];
pair<int, int> pa[N];

int main(){
    scanf("%d", &n);
    for(int i = 1; i <= n; ++i)  scanf("%d%d", &pa[i].first, &pa[i].second), sum += pa[i].first, sum_b += pa[i].second;
    for(int i = 0; i <= n; ++i)
    	for(int t = 0; t <= n; ++t)
    	    for(int p = 0; p <= sum; ++p)
		dp[i][t][p] = -1e9;
    dp[0][0][0] = 0;
    for(int i = 1; i <= n; ++i)
        for(int t = 0; t <= i; ++t)
            for(int p = 0; p <= sum; ++p){
                dp[i][t][p] = dp[i - 1][t][p];
                if(p >= pa[i].first && t > 0)  dp[i][t][p] = max(dp[i][t][p], dp[i - 1][t - 1][p - pa[i].first] + pa[i].second);
	    }
    for(int i = 1; i <= n; ++i){
	double ans = 0;
	for(int t = 1; t <= sum; ++t){
	    ans = max(ans, min(1.0 * t, dp[n][i][t] / 2.0 + sum_b / 2.0));
        }
	printf("%.9lf ", ans);
    }
    return 0;
}
posted @ 2020-12-20 20:49  のNice  阅读(61)  评论(1编辑  收藏  举报