1. 程式人生 > 實用技巧 >2.3記錄結果再利用的動態規劃

2.3記錄結果再利用的動態規劃

動態規劃

記憶化搜尋與動態規劃(揹包問題)

有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;
 }