1. 程式人生 > >動態規劃分析總結——怎樣設計和實現動態規劃算法

動態規劃分析總結——怎樣設計和實現動態規劃算法

基於 進一步 使用 sdn 能夠 疑惑 樓梯 -1 們的

進行算法設計的時候,時常有這種體會:假設已經知道一道題目能夠用動態規劃求解,那麽非常easy找到對應的動態規劃算法並實現;動態規劃算法的難度不在於實現,而在於分析和設計—— 首先你得知道這道題目須要用動態規劃來求解。

本文,我們主要在分析動態規劃在算法分析設計和實現中的應用,解說動態規劃的原理、設計和實現。在非常多情況下,可能我們能直觀地想到動態規劃的算法。可是有些情況下動態規劃算法卻比較隱蔽。難以發現。

本文。主要為你解答這個最大的疑惑:什麽類型的問題能夠使用動態規劃算法?應該怎樣設計動態規劃算法?

動態規劃第一講——緩存與動態規劃


一、緩存與動態規劃


例一:有一段樓梯有10級臺階。規定每一步僅僅能跨一級或兩級,要登上第10級臺階有幾種不同的走法?


分析:非常顯然,這道題的相應的數學表達式是F(n)=F(n-1) + F(n-2);當中F(1)=1, F(2)=2。

非常自然的狀況是,採用遞歸函數來求解:

int  solution(int n){
	if(n>0 && n<2) return n;
	return solution(n-1) + solution(n-2);
}


    假設我們計算F(10), 先須要計算F(9) F(8); 可是我們計算F(9)的時候,又須要計算F(8),非常明顯,F(8)被計算了多次。存在反復計算;同理F(3)被反復計算的次數就很多其它了。算法分析與設計的核心在於 依據題目特點,降低反復計算。  在不改變算法結構的情況下。我們能夠做例如以下改進:
int dp[11];
int  solution(int n){
	if(n>0 && n<2) return n;
	if(dp[n]!=0) return dp[n];
	dp[n] = solution(n-1) + solution(n-2);
	return  dp[n];
}
這是一種遞歸形似的寫法,進一步,我們能夠將遞歸去掉:
int  solution(int n){
	int dp[n+1];
	dp[1]=1;dp[2]=2;
	for (i = 3; i <= n; ++i){
		dp[n] = dp[n-1] + dp[n-2];
	}
	return  dp[n];
}
當然,我們還能夠進一步精簡。只用兩個變量來保存前兩次的計算結果; 這個算法留待讀者自己去實現

例二:01背包問題

有n個重量和價值分別為vector<int> weight, vector<int> value的物品;背包最大負重為W,求能用背包裝下的物品的最大價值?

輸入:n =4
weight=2, 1, 3, 2
value =3, 2, 4, 2
W=5
輸出=7


思考一:我們能夠採用窮舉法,列出n個物品的全部組合形式,從中選取符合條件的最大價值:

採用窮舉法,必定須要可以舉出全部狀態,不重不漏;而怎樣窮舉,方法多種多樣,我們的任務是要窮舉有n個元素組成的全部子集。

而窮舉的方法主要有兩種—— 遞增式(舉出1~100之內的全部數字, 從1到100);和分治式的窮舉(比如舉出n個元素的集合。包括兩種—— 含有元素a和不含元素a的)。於是,我們基於窮舉法得到背包問題的第一種算法—— 遞歸與分治。

int rec(int i, int j){//從i到n號物品,選擇重量不大於j的物品的最大價值
	int res;
	if(i==n){
		res=0;
	} 
	else if(j< w[i]){
		res = rec(i+1, j);
	}
	else{
		res = max(rec(i+1, j), rec(i+1, j-w[i])+v[i]);
	}
	return res;
}

調用res(0, W), 就可以得到結果. 時間復雜度O(2^n);我們來分析一下遞歸調用的情況。

技術分享

為了偷懶,最後一行沒有畫出來,可是註意紅色的部分。我們會

動態規劃分析總結——怎樣設計和實現動態規劃算法