CodeForces - 1459D - Glass Half Spilled

Glass Half Spilled

题意

在编号为 1 1 1 的桌子上有 n n n 个玻璃杯,第 i i i 个杯子可以容纳最多 a i a_i ai 单位水,当前包含 b i b_i bi 单位水。

选择 k k k 个玻璃杯,并尽可能多地收集水。为了达到这个效果,你可以把水从一个玻璃杯倒到另一个玻璃杯,想倒多少次就倒多少次 然而,每次你试图转移任何数量的水时,都会有一半的水洒在地板上。对于每个 k = 1 、 2 、 3 、 … n k=1、2、3、\dots n k=123n,确定在零次或多次在玻璃杯之间转移水后,任意选择的k个玻璃杯可以收集的最大可能总量。

思路

d p ( i , j , k ) dp(i,j,k) dp(i,j,k) 为前 i i i 个杯子中选择 j j j 个杯子 杯子容量上限之和为 k k k 时的最大水量之和

转移方程为 $dp(i,j,k) = $ { d p [ i − 1 ] [ j ] [ k ] d p [ i − 1 ] [ j − 1 ] [ k − a [ i ] ] + b [ i ] \begin{cases}dp\left[ i-1\right] \left[ j\right] \left[ k\right] \\ dp\left[ i-1\right] \left[ j-1\right] \left[ k-a\left[ i\right] \right] +b\left[ i\right] \end{cases} {dp[i1][j][k]dp[i1][j1][ka[i]]+b[i]

s u m b sumb sumb 为初始水量之和

s u m a suma suma 为杯子的容量之和

假设求出最大水量之和为 d p ( i , j , k ) dp(i,j,k) dp(i,j,k) 则转移的水量为 s u m b − d p ( i , j , k ) 2 \dfrac{sumb - dp(i,j,k)}{2} 2sumbdp(i,j,k)

最终的水量为 m i n min min ( d p ( i , j , k ) + s u m b − d p ( i , j , k ) 2 , k ) (dp(i,j,k) + \dfrac{sumb - dp(i,j,k)}{2}, k) (dp(i,j,k)+2sumbdp(i,j,k),k) 同时乘以 2 2 2 并化简得

r e s = m i n ( d p ( i , j , k ) + s u m b , 2 ∗ k ) res = min(dp(i,j,k) + sumb, 2 * k) res=min(dp(i,j,k)+sumb,2k)

输出时记得 r e s / 2 res / 2 res/2

代码

#include<bits/stdc++.h>
#define INF 0x3f3f3f3f
#define mod 1000000007
using namespace std;

typedef long long LL;
typedef pair<int, int>PII;
inline LL gcd(LL a, LL b) { return b ? gcd(b, a % b) : a; }

const int N = 110;

int n;
int a[N], b[N];
double dp[2][N][N * N];

void solve() {
	cin >> n;
	int suma = 0, sumb = 0;
	for (int i = 1; i <= n; ++i) {
		scanf("%d %d", &a[i], &b[i]);
		suma += a[i];
		sumb += b[i];
	}
    //预处理
	for (int j = 0; j <= n; ++j)
		for (int k = 0; k <= suma; ++k)
			dp[0][j][k] = -INF;
	dp[0][0][0] = 0;
	//
    
	int t = 1;//滚动数组
	for (int i = 1; i <= n; ++i,t ^= 1) {

		for (int j = 0; j <= n; ++j) //不选第i个杯子
			for (int k = 0; k <= suma; ++k)
				dp[t][j][k] = dp[t ^ 1][j][k];

		for (int j = 1; j <= n; ++j) //选第i个杯子
			for (int k = a[i]; k <= suma; ++k)
				dp[t][j][k] = max(dp[t][j][k], dp[t ^ 1][j - 1][k - a[i]] + b[i]);

	}
	for (int j = 1; j <= n; ++j) {
		double res = 0;
		for (int k = 0; k <= suma; ++k)
			res = max(res, min(double (2.0 * k), dp[t ^ 1][j][k] + sumb));
		printf("%.10lf ", res / 2);
	}
	cout << endl;
}
int main() {
	//int t; cin >> t;
	//while (t--)
		solve();

	return 0;
}
  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

zzqwtc

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值