題目連接c++
分析:典型的01揹包問題,設dp[i][j]爲空間(也就是題面中的時間)是j的揹包在裝前i個物品(草藥)所得的最大價值,v[i]爲第i個物品的重量(採藥的時間),w[i]爲第i個物品(草藥)的價值,則有:數組
當j>v[i]時,dp[i][j]=max{dp[i-1][j],dp[i-1][j-v[i]]+w[i]}優化
當j<=v[i]時,dp[i][j]=dp[i-1][j]spa
接下來,咱們就來詳細解析一下咱們的前輩是怎樣獲得這個公式的。(知道的能夠跳過)code
假設咱們如今有這樣一組數據:blog
10 4get
3 4it
4 7class
6 11im
8 16
咱們須要列一個表格,來模擬dp數組變化的過程。(不要使用上面的狀態轉移方程,要手推,這樣才能理解這個方程!!)
若是你模擬的沒錯的話,表格最後會是這樣的:
咱們發現,dp[i][j]總比上面的dp[i-1][j]多(或相等),這是爲何呢?(請仔細想一想,在閱讀下面的文字)
由於,dp[i][j]表示的是空間是j的揹包在裝前i個物品所得的最大價值,少裝一個物品(也有可能裝的同樣)不可能比多裝一個物品的價值高,對吧?O(∩_∩)O
由於上一行(dp[i-1][j])已經把能裝的都裝了,因此咱們只須要考慮當前物品是否能裝的下當前的揹包就能夠了。
因此,有了這個神奇的狀態轉移方程:
當j>v[i]時,f[i][j]=max{f[i-1][j],f[i-1][j-v[i]]+w[i]}
當j<=v[i]時,f[i][j]=f[i-1][j]
ok,說了這麼多,終於到了你們期待的代碼環節:
獻上本蒟蒻的代碼:
#include<bits/stdc++.h> using namespace std; int main() { int n,m; int w[105],v[105],dp[105][1005]; memset(dp,0,sizeof(dp)); int i,j; scanf("%d %d",&m,&n); for(i=1;i<=n;i++) scanf("%d %d",&v[i],&w[i]); for(i=1;i<=n;i++) { for(j=0;j<=m;j++) { if(j<v[i]) dp[i][j]=dp[i-1][j]; else dp[i][j]=max(dp[i-1][j],dp[i-1][j-v[i]]+w[i]); } } printf("%d\n",dp[n][m]); return 0; }
可是------------------
這個代碼是能進行空間優化的!(雖然這道題不須要)
考慮到狀態轉移方程只對上一行(dp[i-1][j])進行操做,因此咱們能夠將dp數組從這樣:
dp[105][1005]
變成這樣:
dp[1005](詳見代碼)
這,就是傳說中的滾動數組。
固然,優化了空間以後,中間的操做也須要一些特殊的操做。(詳見代碼)
獻上本蒟蒻的滾動數組代碼:
#include<bits/stdc++.h> using namespace std; int main() { int n,m; int w[105],v[105],dp[1005]; memset(dp,0,sizeof(dp)); int i,j; scanf("%d %d",&m,&n); for(i=1;i<=n;i++) scanf("%d %d",&v[i],&w[i]); for(i=1;i<=n;i++) for(j=m;j>=0;j--) if(j>=v[i]) dp[j]=max(dp[j-v[i]]+w[i],dp[j]); printf("%d\n",dp[m]); return 0; }
評測結果:
不加空間優化:
加滾動數組空間優化:
雖然好像沒什麼變化