1. 程式人生 > 實用技巧 >Codeforces Round #691 (Div. 2) D - Glass Half Spilled(DP)

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;
}