題解 P5662 [CSP-J2019] 紀念品
阿新 • • 發佈:2021-06-23
做到這個題的時候人都傻了,被鵬哥批了一頓後,直接大叫:“ \(woc!\) ,這不就一完全揹包嗎!”
好吧,讓我們看看這個題怎麼和揹包扯上關係的。
進入正題
第一眼,嗯, \(DP\)。
想了一會以後發現想不出來,好吧,看一下部分分。
\(T=1\) ,直接就是 \(m\) ,沒什麼用(蹭點分還是很香的)
再看,誒,\(T=2\),好傢伙,看一下買哪個賺的最多,直接買了,如果還有餘錢買第二賺的,繼續買……當買不了了,第二天統統賣掉,此時就可以得到答案。
再想一想,如果把這兩天結束後的錢當做新一輪開始時的本錢,是不是隻要一直重複同樣的步驟就能得出答案?
完全沒毛病啊!而且只要保證每一輪得到最大的錢數(區域性最優解),就一定可以保證整體的最優解。
注意到題目中有這句話:“每天賣出紀念品換回的金幣可以立即用於購買紀念品,當日購買的紀念品也可以當日賣出換回金幣。”也就是說,我們可以每天先把手頭的紀念品全賣掉,然後重新購買,完美契合上面的想法。
如何滿足區域性最優解呢?物品數量不限,總價值有限制,這不就一完全揹包嗎!
-
把當前擁有的錢數看做揹包的體積。
-
把物品的價格看做取該物品的代價。
-
把物品在第二天賣掉時賺的錢當做價值。
完美的一個完全揹包!
細節看程式碼:
#include<bits/stdc++.h> #define rd(n) scanf("%d",&n); #define F(i,a,b) for(register int i=a;i<=b;i++) using namespace std; const int N=1e4+10; int t,n,m,p[1001][1001],dp[N]; int main(){ rd(t)rd(n)rd(m) F(i,1,t){ F(j,1,n){ rd(p[j][i]) } } F(k,1,t-1){ memset(dp,0,sizeof(dp));//每一輪開始前清空 F(i,1,n){ F(j,p[i][k],m){//完全揹包,正著做 dp[j]=max(dp[j],dp[j-p[i][k]]+p[i][k+1]-p[i][k]); //p[i][k+1]-p[i][k]是兩天的差價(即選該物品得到的價值) } } m+=dp[m];//將當前一輪結束時的區域性最優解轉化為下一輪的本金 } printf("%d",m); return 0; }