1. 程式人生 > 其它 >題解 P5662 [CSP-J2019] 紀念品

題解 P5662 [CSP-J2019] 紀念品

題目傳送門

做到這個題的時候人都傻了,被鵬哥批了一頓後,直接大叫:“ \(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;
}

AC

完結撒花!