2.3記錄結果再利用的動態規劃
阿新 • • 發佈:2020-07-14
動態規劃
記憶化搜尋與動態規劃(揹包問題)
有n個重量和價值分別為wi,vi的物品,從這些中挑選總重量不超過W的物品,求所選方案中價值總和最大值
#include<bits/stdc++.h> using namespace std; const int maxn=1e2+5 ; int w[maxn],v[maxn]; int n,W ; int rec(int i,int j) { int ans; if(i==n) ans=0;//無剩餘用品 else if(j<w[i])ans=rec(i+1,j);//不能選取 else{ans=max(rec(i+1,j),rec(i+1,j-w[i])+v[i]);}//評判選與不選那個更划算 return ans; } int main() { cin>>n; for(int i=0;i<n;i++) cin>>w[i]>>v[i]; cin>>W ; printf("%d\n",rec(0,W )); }
可以看到的是,此方法使得每一層搜尋都有兩次分支,使得時間複雜度達到了O(2^n)。但在此遞迴中會出現較多的重複呼叫,這也是我們簡化、加快計算時間的突破口——將第一次計算的結果都記錄下來,這樣就能避免多次計算。
這種方法也叫記憶化搜尋。只有在第一次被呼叫時執行遞迴部分,將複雜度縮減到了O(nW)
int dp[maxn+1][maxn+1]; int rec(int i,int j) { if(dp[i][j]>=0) return dp[i][j];//用dp陣列進行儲存 int ans; if(i==n) ans=0; else if(j<w[i])ans=rec(i+1,j); else{ans=max(rec(i+1,j),rec(i+1,j-w[i])+v[i]);} return dp[i][j]=ans; }
memset陣列初始化
按照1位元組為單位對記憶體進行填充(不能初始化為1之類的數值!!!)
memset(dp,-1,sizeof(dp));//-1√ 0√ inf√ -inf√
又因為可以寫出遞推式:
dp[n][j]=0
dp[i][j]={max(dp[i+1][j],dp[i+1][j]-w[i])+v[i]) //其他
{dp[i+1][j] //j<W[i]
所以也可以直接寫作一個二重迴圈。
int DP[maxn+1][maxn+1]; void solve () { /*for(int j=0;j<=W ;j++) DP[n][j]=0;*/ //全域性陣列會被初始化為0 for(int i=n-1;i>=0;i--) for(int j=0;j<=W ;j++) { if(j<w[i]) DP[i][j]=DP[i+1][j]; else DP[i][j]=max(DP[i+1][j],DP[i+1][j]-w[i])+v[i]); } cout<<DP[0][W ]<<endl; }