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
小结
背包模型的灵活转化

浙公网安备 33010602011771号