TopCoder SRM 478 Div 1 Medium KiwiJuice
問題概要
容量Cのボトルに、それぞれbottle[i]のジュースが入っている。
以下の操作を0回以上任意に行い、ジュースを売るときの、最大の利益を求めよ。
ただし、xリットルのジュースの入ったボトルはprice[x]で売れる。
操作:
好きな2本の相違なるボトルA,Bを選び、Aが満タンかBが空になるまでBのジュースをAに注ぐ。
C≦50,
ボトルの本数≦15を満たす。
解法
sigmarさんの解説(g:topcoder:id:jackpersel:20100804)がわかりやすかった。
ボトルをk本選んでお互いに注ぐ場合、1本の空でも満タンでもないボトルができ、残りは全て空か満タン。かつ、それは順番に拠らない。
↑なんか僕はこういう「順番に拠らない」とかを見つけるのが弱い。
250もそうだけどさー。
すると、
(ある最適解)=(その中のボトルの一部を混ぜる)+(残りの最適解)
という関係からビットDPができる。
ある集合の中から部分集合を作る方法はwriterさんのコードのが目から鱗だった。
ソースコード
writerさんのほぼそのまま。
class KiwiJuice { public: int theProfit(int C, vector <int> bottles, vector <int> prices) { int n=bottles.size(),mazeru[32768]={0}; rep(i,1<<n) { int sum=0,btl=0; rep(j,n)if(i&1<<j)sum+=bottles[j],btl++; mazeru[i]=prices[C]*(sum/C)+prices[0]*(btl-sum/C-1)+prices[sum%C]; } int dp[32768]={0}; rep(i,1<<n)for(int j=i;j>0;j=(j-1)&i)dp[i]=max(dp[i],dp[i^j]+mazeru[j]); return dp[(1<<n)-1]; } };