B. Glass Half Spilled_思维+背包

B. Glass Half Spilled_思维+背包

2000

题目大意

给n个水杯,每个杯子有不同的最大容量ai和现在的水量bi 。每次可以选择任意一个杯子,把该杯子里的水倒x单位到另外一个杯子里。但是这些杯子很奇怪,倒出x单位的水,倒到另外一个杯子里只有x/2单位(浮点数)。现问经过若干次操作后,i杯水的和的最大值是多少(i属于1~n)

思路和代码

首先,考虑三个杯子。a杯的水倒x单位到b中只剩下x/2单位。如果我们要把b中的水再倒到c杯子,那么就只有x/4单位。这样的话不如直接从a杯倒到c杯。所以,每一份水最多倒一次。

再考虑,如果我们选定了i个杯子,那么最好的情况就是把其余n-i个杯子里的水全部倒进这i个杯子里。(溢出也没事)

所以不妨设dp[i,j,k]为前i个杯子里选择j个,且这j个杯子的容量为k的最大水量和。

可以发现这是一个简单的01背包,所以可以将第一维i优化掉。

最后的答案就是选择i个杯子,然后将其余杯子里的水倒到这i个杯子里。
$$
ans=dp_{i,j}+(sum-dp_{i,j})/2
$$
但是我设置的数组是整数,所以稍微改了一下,把上面式子乘了2再化简就得到:
$$
ans*2=sum+dp_{i,j}
$$

/*
一个杯子分出去的水不会再被分到另外一个杯子
dp[i,j,k]表示前i个杯子中选择j个,总容量为k,可以容纳的最大水量 

杯子有容量限制
倒水只能倒一半 

如果有选择了j个杯子,那么就是要把n-j个杯子里的水倒到j个里面去。
而且根据上面的分析,每个杯子只会倒水一次 
now[j] = min(Vj , now[j] + now[n-j]/2) 

所以要知道选择了哪几个,这样的话复杂度会到2e100(并不需要)

将总水量随着dp转移可以维护Vj (并不需要)
*/
//以上注释是我做题时的思维过程,不是正解
void solve(){
	cin >> n ; 
	
	vct<ll> a(n + 1 , 0) ;
	vct<ll> b(n + 1 , 0) ;

	vct<vct<ll> > dp(n + 1 , vct<ll>(10010 , -INF)) ;
	dp[0][0] = 0 ;
	rep(i , 1 , n) cin >> a[i] >> b[i] ;

	ll sum = accumulate(b.begin() + 1 , b.end() , 0LL) ;
	
	rep(i , 1 , n)
	drep(j , 1 , i)
	drep(k , a[i] , 10001)
	dp[j][k] = max(dp[j][k] , dp[j - 1][k - a[i]] + b[i]) ;
	
	rep(i , 1 , n){
		ll ans = 0 ;
		rep(j , 1 , 10001)
		ans = max(ans , min(2LL * j , sum + dp[i][j])) ;
		cout << fixed << setprecision(10) << 1.0 * ans / 2 << " " ;
	}
	
}//code_by_tyrii 

小结

背包模型的灵活转化

posted @ 2022-05-11 15:47  tyrii  阅读(54)  评论(0)    收藏  举报