洛谷 P5662 紀念品 & [NOIP2019普及組] (dp,完全背包)


傳送門


解題思路

本題首先要明白,在每一天時,最優策略是先進行操作2(賣),再進行操作1(買),才能是利益最大化。

本題很顯然當只有兩天時,是一個完全背包,就是把當日價錢當做體積,把明日價格和今日價格的差作為價值,跑一邊完全背包即可。時間復雜度O(TNM)

然后我們考慮滿分做法——我們用dp[j]表示還剩下j個體積時,所能獲得的最大利益。

然后對於后面的所有天,兩天跑一遍完全背包。每一次統計時答案就是dp[m]+m,即利潤+本金。

這里一定要注意對於每一天來說,購買的方式對以后的賺錢並未影響,所以只需記錄下一天下來最大的錢數(即明日的本金)即可。然后還要清空dp數組。

還有一個重點就是對於每一樣物品,在第x天買入k件,第x+1天賣出k件,又買入k件,第x+2天又買入k件,這個過程其實就相當於在第x天買入k件,在x+2天賣出k件,所以我們枚舉所有的可能不受限制(只不過多算了幾步罷了)

這里還可以這樣理解(貪心):就是在當天買的物品,第二天必須全部賣出去。如果不賣是划算的,那么再買回來即可。

還有一個地方比較難理解:為什么我們不用記錄怎樣買的呢?在第x天時不會賣出去一些手上沒有的紀念品嗎?

答案時否定的。Why?

看代碼,當我們買某件紀念品的時候,我們加上的價值是(明日價格-今日價格),從數值上講是加上明天接着賣出的話賺的利潤,感性理解的話是今天買的這幾件物品在第二天一定全部賣出去。所以每天的操作實際上只有1操作(買)。

很顯然,只要錢足夠,可以買進無限個。

AC代碼

 1 #include<iostream>
 2 #include<cstdio>
 3 #include<cmath>
 4 #include<cstring>
 5 using namespace std;
 6 const int maxn=105;
 7 const int maxm=10005;
 8 int T,n,m,p[maxn][maxn],dp[maxm];
 9 //第t天買到第i件物品時剩余容量是j元的最大利潤。 
10 int main()
11 {
12     cin>>T>>n>>m;
13     for(int i=1;i<=T;i++){
14         for(int j=1;j<=n;j++) {
15             scanf("%d",&p[i][j]);
16         }
17     }
18     for(int t=1;t<=T;t++){
19         memset(dp,0,sizeof(dp));
20         for(int i=1;i<=n;i++){
21             for(int j=p[t][i];j<=m;j++){
22                 dp[j]=max(dp[j],dp[j-p[t][i]]+p[t+1][i]-p[t][i]);
23             }
24         }
25         m=max(m,dp[m]+m);
26     }
27     cout<<m;
28     return 0;
29 }

//CSP2019普及組 t3


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM