fjnu2019第二次友誼賽 F題

### 題目連接 ###php

 

題目大意:html

 

一開始手上有 z 個錢幣,有 n 天抉擇,m 種投資方案,在天天中能夠選擇任意種投資方案、任意次地花費 x 個錢幣(手上的錢幣數不能爲負),使得在 n 天結束後,得到 y 個錢幣。ios

 

其次,在天天結束後,會根據本身手上所具備的節點數來得到一些錢幣補償,設當天結束後所擁有 x 個錢幣,那麼將得到 f(x) 個錢幣,若 x > k ,則默認爲 0 。保證 f[0]~f[k] 單調不增。優化

 

這題的原題:博客鏈接 ,只是一開始手上的錢數不一樣而已。
spa

 

分析:code

一、將全過程分爲 n 天,在 dp 枚舉天天的狀態時,先處理當天一開始所擁有的錢幣數(即上一天結束後手上的錢數),再進行當天的投資。因此咱們將當天投資結束後的補償狀態在下一天的早上進行轉移,而不是在當天結束後進行狀態轉移。htm

二、故設 dp[i][j] 表示在第 i 天投資完以後(當天還未得到補償),在手上擁有 j 個錢幣時,在 n 天結束後所得到的返還的最大錢幣總數。blog

三、那麼對於每一天的一開始錢幣數是由上一天補償以後而轉移過來的,因爲補償只會在有剩下 0 ~ k 個錢幣時纔會發生 (即 手上錢幣數爲 0 ~ k 時纔可能得到補償),故在天天開始前須要遍歷上一天手中剩下的錢幣數,得到必定的補償以後,成爲今天一開始的錢幣數。ci

四、得到當天的錢幣數後,開始進行今天的投資狀態轉移。因爲投資的數量爲任意次,故爲徹底揹包的轉移。get

五、若對於當天投資結束後手上有 j 元時的狀態( 即 dp[i][j] ),它必從當天投資一開始的手上錢幣爲 ( j + 投資成本 )時,花費投資成本後轉移而來。故若設投資成本爲 w ,最後一天返還 v ,則有 dp[i][j] = dp[i][j + w] + v ,徹底揹包取最大值便可。

六、因爲一開始有錢幣數,而按上面所述,第一天一開始不能從上一天剩餘錢幣數上轉移,故第一天須要單獨拿出來先處理。

七、初始化問題:按理這題 dp 須要求最大值,所有初始化爲 0 纔對,可是須要保證本題的實際意義——須要從第一天手上 z 個錢幣時轉移,故須要初始化所有爲負無窮,而後將dp[1][z] 賦值爲 0 ,代表 dp 轉移時,必須從第一天手上有 z 個錢幣時轉移而來。

八、因爲咱們將第一天枚舉單獨拿出來了,即意味着求的全部狀態都必須在第一天買東西纔會被轉移到次日(即第一天啥都不買時,這個狀態不會被傳遞下去),而後再進行下面的天天轉移。故咱們求答案時,還須要判斷是否 n 天啥都不投資的錢幣會更多(意思是若是第一天啥都不買,最大值不可能輪到次日纔開始買)。

 

注意點:

一、若按上述這樣直接作的話,複雜度接近 n³ * k (k 爲常數),此題數據將會有 3000MS (固然出題人良心放寬時限)。

二、若要下降時間複雜度,須要知道徹底揹包去掉枚舉方案個數的原理。這個原理實際上是當前 dp[i][j] 從本層以前已更新過的狀態轉移過來(詳細則本身百度或羣裏問)。

三、此題與普通徹底揹包降維不一樣的是,分析 dp 轉移方程,發現他是從後往前轉移的 (即 dp[i][j] 中的 j 從 j + w 上轉移而來),優化時,須要反向枚舉揹包容量。

 

無優化代碼:

#include<iostream> #include<algorithm> #include<string.h>
using namespace std; int z,N,M,K; int dp[108][2008]; int f[1008]; struct Goods{ int a,b; }A[108]; int main() { scanf("%d%d%d%d",&z,&N,&M,&K); for(int i=0;i<=K;i++) scanf("%d",&f[i]); for(int i=1;i<=M;i++) scanf("%d%d",&A[i].a,&A[i].b); memset(dp,0x80,sizeof(dp)); dp[1][z]=0; for(int w=1;w<=M;w++){ for(int j=A[w].a;j<=2000;j++){ for(int k=0;k<=j/A[w].a;k++){ dp[1][j-k*A[w].a]=max(dp[1][j-k*A[w].a],dp[1][j]+k*A[w].b); } } } for(int i=2;i<=N;i++){ for(int j=0;j<=K;j++) dp[i][j+f[j]]=max(dp[i][j+f[j]],dp[i-1][j]); for(int w=1;w<=M;w++){ for(int j=A[w].a;j<=2000;j++){ for(int k=0;k<=j/A[w].a;k++){ dp[i][j-k*A[w].a]=max(dp[i][j-k*A[w].a],dp[i][j]+k*A[w].b); } } } } int ans=z; for(int i=0;i<=2000;i++) ans=max(ans,dp[N][i]+i+f[i]); printf("%d\n",ans); }

 

優化代碼:

#include<iostream> #include<algorithm> #include<string.h>
using namespace std; int z,N,M,K; int dp[108][2008]; int f[1008]; struct Goods{ int a,b; }A[108]; int main() { scanf("%d%d%d%d",&z,&N,&M,&K); for(int i=0;i<=K;i++) scanf("%d",&f[i]); for(int i=1;i<=M;i++) scanf("%d%d",&A[i].a,&A[i].b); memset(dp,0x80,sizeof(dp)); dp[1][z]=0; for(int w=1;w<=M;w++){ for(int j=2000;j>=A[w].a;j--){ dp[1][j-A[w].a]=max(dp[1][j-A[w].a],dp[1][j]+A[w].b); } } for(int i=2;i<=N;i++){ for(int j=0;j<=K;j++) dp[i][j+f[j]]=max(dp[i][j+f[j]],dp[i-1][j]); for(int w=1;w<=M;w++){ for(int j=2000;j>=A[w].a;j--){ dp[i][j-A[w].a]=max(dp[i][j-A[w].a],dp[i][j]+A[w].b); } } } int ans=z; for(int i=0;i<=2000;i++) ans=max(ans,dp[N][i]+i+f[i]); printf("%d\n",ans); }
相關文章
相關標籤/搜索