傳送門
解題思路
本題首先要明白,在每一天時,最優策略是先進行操作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