补下因实验漏掉的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;
}