[Usaco2008 Dec]Hay For Sale 購買幹草
阿新 • • 發佈:2019-05-10
iostream 時間復雜度 while 答案 格式 維數 line str spa
很水的01背包裸題,敲著練手感。以免被人說成是大水怪,還是
題目描述
約翰遭受了重大的損失:蟑螂吃掉了他所有的幹草,留下一群饑餓的牛.他乘著容量為C(1≤C≤50000)個單位的馬車,去頓因家買一些幹草. 頓因有H(1≤H≤5000)包幹草,每一包都有它的體積Vi(l≤Vi≤C).約翰只能整包購買,
他最多可以運回多少體積的幹草呢?
輸入格式
第1行輸入C和H,之後H行一行輸入一個Vi.
輸出格式
最多的可買幹草體積.
很水的01背包裸題,敲著練手感。以免被人說成是大水怪,還是 象征性地 講講01背包的做法......
設dp(i,j)表示裝到第i個物品,用去了j個單位體積時的最大價值;c[i]為第i個物品的重量,v[i]表示第i個物品的價值。那麽顯然:
\[ dp(i,j)= \left\{\begin{array}{rcl} max(dp(i-1,j-c[i])+v[i],dp(i-1,j)) & j>=c[i]\ dp(i-1,j) & j<c[i] \end{array}\right. \]
於是我們填一遍表就可以了,最終答案為dp(n,m).
可以發現對當前計算有用的只有第i層和第i-1層dp數組,之前的都已經沒用了,所以可以用滾動數組優化一下:
\[ dp(d,j)= \left\{\begin{array}{rcl} max(dp(!d,j-c[i])+v[i],dp(!d,j))&j>=c[i]\ dp(!d,j)&j<c[i] \end{array}\right. \]
其實每次j下標的值都是由j-c[i]的值更新過來的,如果我們保證j下標比j-c[i]先更新,我們是可以用一維數組的。具體就是:把循環倒過來做。
\[ dp[j]=max(dp[j-c[i]]+v[i],dp[j]) \]
其時間復雜度為:
\[ O(NM) \]
空間復雜度為:
\[ O(m) \]
那麽這題只需要求能夠運回多少體積的幹草,簡直就是01背包的弱化版:
\[ dp[j]|=dp[j-c[i]] \]
初始化dp[0]=true,最後循環找一下最大的j,其dp[j]=true即可。
#include<iostream> #include<cstring> #include<cstdio> #define maxn 5001 #define maxm 50001 using namespace std; bool dp[maxm]; int n,m,c[maxn]; inline int read(){ register int x(0),f(1); register char c(getchar()); while(c<'0'||'9'<c){ if(c=='-') f=-1; c=getchar(); } while('0'<=c&&c<='9') x=(x<<1)+(x<<3)+(c^48),c=getchar(); return x*f; } int main(){ m=read(),n=read(); for(register int i=1;i<=n;i++) c[i]=read(); dp[0]=true; for(register int i=1;i<=n;i++){ for(register int j=m;j>=c[i];j--) if(dp[j-c[i]]){ dp[j]=true; } } for(register int i=m;i>=0;i--) if(dp[i]){ printf("%d\n",i); break; } return 0; }
為什麽我要給水題寫這麽長的題解
[Usaco2008 Dec]Hay For Sale 購買幹草