轉【算法之動態規劃(四)】動態規劃筆試例題詳解

 

終於來到了算法設計思想中最有趣的這部分,在去年的google筆試中,7道算法設計題有2道動態規劃(Dynamic Programming)。

看了這麼久的算法,這部分也是惟一感受到了比較難的地方,

從這篇文章開始,將花連續的篇幅來討論一些對動態規劃的認識和其中的問題。這包括一些例子:計算二項式係數,Warshall算法求傳遞閉包,Floyd算法求徹底最短路徑,構造最

有二叉查找樹,揹包問題和記憶功能。也包括一些其餘問題的解題報告(動態規劃確實很難,對這一章的內容,我將搜索一些其餘類型的問題來寫解題報告,以真正的

理解動態規劃),例如矩陣連乘,最長公共子列,等等。



--------------------------------------------------------------------------------------------------------------------------------------------------


1,什麼是動態規劃(DP)?


很是重要!,不要認爲概念不重要,理解的深入,你才知道對於什麼樣的問題去考慮有沒有動態規劃的方法,以及如何去使用動態規劃。


1)動態規劃是運籌學中用於求解決策過程當中的最優化數學方法。 固然,咱們在這裏關注的是做爲一種算法設計技術,做爲一種使用多階段決策過程最優的通用方法。

它是應用數學中用於解決某類最優化問題的重要工具。


2)若是問題是由交疊的子問題所構成,咱們就能夠用動態規劃技術來解決它,通常來講,這樣的子問題出如今對給定問題求解的遞推關係中,這個遞推關係包含了相

同問題的更小子問題的解。動態規劃法建議,與其對交疊子問題一次又一次的求解,不如把每一個較小子問題只求解一次並把結果記錄在表中(動態規劃也是空間換時間

的),這樣就能夠從表中獲得原始問題的解。


關鍵詞:

它每每是解決最優化問題滴

問題能夠表現爲多階段決策(去網上查查什麼是多階段決策!)

交疊子問題:什麼是交疊子問題,最有子結構性質。

動態規劃的思想是什麼:記憶,空間換時間,不重複求解,由交疊子問題從較小問題解逐步決策,構造較大問題的解。


-------------------------------------------------------------------------------------------------------------------------------------------------


關於斐波拉切數列能夠做爲最簡單的一個例子來解釋動態規劃的思想,在前面講斐波拉切數列時說過了,再也不敘述。


通常來講,一個經典的動態規劃算法時自底向上的(從較小問題的解,由交疊性質,逐步決策處較大問題的解),它須要解出給定問題的全部較小子問題。動態規劃的

一個變種是試圖避免對沒必要要的子問題求解。若是採用自頂向下的遞歸來解,那麼就避免了沒必要要子問題的求解(相對於動態規劃表現出優點),然而遞歸又會致使對

同一個子問題屢次求解(相對於動態規劃表現出劣勢),因此將遞歸和動態規劃結合起來,就能夠設計一種基於記憶功能的從頂向下的動態規劃算法,在後面會講。



------------------------------------------------------------------------------------------------------------------------------------------------


計算二項式係數:

在排列組合裏面,咱們有下面的式子(很容易用組合的定義來證實):



這個式子將C(n , k)的計算問題表述爲了(問題描述)C(n-1 , k -1)和C(n -1, k)兩個較小的交疊子問題。

初始條件:C(n , n) = C(n , 0) = 1

咱們能夠用下列填矩陣的方式求出C(n , k):



該算法的時間複雜度是多少呢?能夠大概的估計下,只填了下三角矩陣,爲n*k/2  =  n*k,具體的次數爲:



矩陣怎麼填(填矩陣的順序)?

按行來填矩陣:算法僞代碼:



第1個for是控制行的,要填到第n行。第2個for來控制每行填到哪的,到i和k的較小值。從這2個for也能夠看出複雜度是n*k。

實現:

html

複製代碼
package Section8;


/*第八章 動態規劃 計算二項式係數*/

publicclass BinoCoeff {

/**
* @param args
*/
publicstaticvoid main(String[] args) {
// TODO Auto-generated method stub
int result = Binomial(8,3);
System.out.println("輸出8的二項式係數:");
for(int i =0;i <=8;i++)
System.out.println("C"+"("+8+","+ i +")"+" ———— "+ Binomial(8,i));
}

publicstaticint Binomial(int n,int k){
//計算二項式係數C(n,k)
int[][] result =newint[n+1][n+1];
for(int i =0;i <= n;i++) //按行來填矩陣
{
for(int j =0;j <= min(i,k);j++) // min(i,k)是這一行須要填的列數
{
//if(j == 0 || j == k)//書上寫錯了,他媽的
if(j ==0|| j == i)
result[i][j] =1;
else
result[i][j] = result[i -1][j -1] + result[i -1][j];
}
}

return result[n][k];
}

privatestaticint min(int i,int k){
if(i < k)
return i;
return k;
}

}
複製代碼



結果:

輸出8的二項式係數:
C(8,0) ———— 1
C(8,1) ———— 8
C(8,2) ———— 28
C(8,3) ———— 56
C(8,4) ———— 70
C(8,5) ———— 56
C(8,6) ———— 28
C(8,7) ———— 8
C(8,8) ———— 1

其實能夠返回整個矩陣,這樣就能夠一次把全部的C(n , k)都計算出來。



--------------------------------------------------------------------------------------------------------------------------------------------------


再看動態規劃:

上面棕色字體標出的就是一個動態規劃算法的幾個關鍵點:

1)怎麼描述問題,要把問題描述爲交疊的子問題

2)交疊子問題的初始條件(邊界條件)

3)動態規劃在形式上每每表現爲填矩陣的形式(在後面會看到,有的能夠優化空間複雜度,開一個數組便可,優化也是根據遞推式的依賴形式的,後面有篇文章詳細說明)

4)填矩陣的方式(或者說順序)代表了什麼?--它代表了這個動態規劃從小到大產生的過程,專業點的說就是遞推式的依賴形式決定了填矩陣的順序




---------------------------------------------------------------------------------------------------------------------------------------------------




習題8.1   決定這一章的習題都認真的作一遍






1

a,相同點是動態規劃和分治法都劃分爲了較小規模問題的解

b,不一樣點是動態規劃的較小子問題是交疊的,並且要存儲較小子問題的解


2

a,參見代碼,已實現

b,也能夠按列來填矩陣(想一想爲何?)---實際上這個問題就代表了再看動態規劃第四點(填矩陣的方式代表了什麼)


3

easy,在講解中已多處指出


4

a, 空間效率也是nk,參見代碼,或者從矩陣上也可看出

b,能夠,這個問題的代表了再看動態規劃第三點(優化空間複雜度)

---爲何能夠優化,上面說過,可不能夠優化,以及如何優化空間複雜度依賴於它的遞推形式:

---從填矩陣的那張圖能夠看出,這個動態規劃產生各項的過程(若是按行填的話)是上一行的第 i-1 項和第 i 項加起來產生下一行的第 i 項,傳統上,咱們從左往右填。

---事實上,根據它的產生過程(這個產生過程依賴於遞推式自身的數學特徵),能夠從右往左填,這樣開一個數組就行,在原數組上本地不動的填數,從右往左填可

以保證一個位置在覆蓋之後不會再被用到(這是由遞推式的屬性決定的,須要畫一畫纔看的比較清楚)。


這樣開一個K大的數組就好了,具體的實現就不寫了,已經分析的很清楚了,實現也不難



---------------------------------------------------------------------------------------------------------------------------------------------------


由以上分析,加習題,相信對於動態規劃究竟是什麼,核心思想,具體的操做細節,以及對於動態規劃的理解都加深了吧,

有2點我以爲很是重要,一是填矩陣的順序,二是動態規劃空間複雜度的優化:這2點都跟遞推式的依賴關係有關(這是本質),在形式上就表現爲填矩陣的時候你的

順序要確保每填一個新位置時你所用到的那些位置(即它依賴的)要已經填好了,在空間優化上表現爲當一個位置在之後還有用的時候你不能覆蓋它


這2條結論很是重要,是深入理解動態規劃的一個重要階梯,我也是很久慢慢悟出來的,固然,它們有更professional的表述,在下一篇文章裏我會貼出。


---------------------------------------------------------------------------------------------------------------------------------------------------


再來看2道實踐的習題吧,也比較簡單:



9,靠,電子版的居然跟紙質版的不太同樣,電子版書上沒有這個題,截不了圖,抄一下吧:


問題:

國際象棋中的車能夠水平的或豎直的移動,一個車要從一個棋盤的一角移到對角線的另外一角,有多少種最短路徑?

a,用動態規劃算法求解

b,用初等排列組合知識求解


b)先說b吧,這是個很簡單的高中排列組合題目了,假設棋盤大小是n*n的(囧,象棋棋盤多大這個得想一想才知道,就說n吧),答案是C(2n , n)


a)用a方法作下吧(主要是培養下怎麼去創建動態規劃的遞推式)

問題是從(0,0)移動到(n,n)有多少種方法?(最短路,即橫n豎n,不能回退)


設C[i , j]表示從(0,0)移動到(i ,j)的方法數(描述問題,怎麼去刻畫C[i , j]的含義,是動態規劃的一個關鍵點):

那麼怎麼才能走到(i ,j)呢,它的上一步一定是(i-1 ,j)或者(i ,j-1)-------(分析動態規劃問題的逆向思惟,很重要,後面要講)

這樣就將問題描述爲了交疊子問題:

C[i , j]  =  C[i -1, j]   +  C[i , j-1]        ( C[i , j]的含義  )


咱們要求的是C[n , n]


初始條件:

C[0 , j]  =  j           j從0到n  

C[i , 0]   =  i           i從0到n

即第一行第一列肯定。


填矩陣的形式:能夠按行也能夠按列。


以上分析畫個圖很容易看出來。剩下的實現就很簡單了。




10





第一問就是個機率題,聽起來比較拗口,其實不難,屬於高中機率水平:



有了遞推式後,發現其實跟上一題徹底同樣,就是遞推式裏多乘了個機率值,難怪電子版省略了一個題,剩下的略


--------------------------------------------------------------------------------------------------------------------------------------------------


很是重要:

解這兩道題後,應該知道一個動態規劃的設計過程是怎樣的:

我的體會是動態規劃的難點在於前期的設計:


a)怎麼描述問題,使它能表述爲一個動態規劃問題(具有什麼特徵?最有子結構,多階段決策,思考)

b)遞推式的寫出(逆向思惟去分析或正向思惟去遞歸),肯定你要求的是哪一個值

c)有了遞推式能夠畫個矩陣的圖(通常只從式子上不太容易看出來,固然,對於牛人來講能夠藐視),在圖中關注如下兩點:

初始條件

填矩陣的順序(即怎麼去寫代碼控制語句)


有了這些以後,其實動態規劃的代碼都很簡單,它的難點在於問題的描述和解決階段,而不在於寫代碼的階段,剩下的寫代碼基本上就是照着公式填矩陣。



--------------------------------------------------------------------------------------------------------------------------------------------------


差點把一個重要的問題忘了:

咱們來看看象棋問題的動態規劃描述,它爲何能夠描述爲動態規劃的?

關於能夠描述爲交疊子問題,上面分析過了,


咱們再說下最有子結構和多階段決策

最優子結構:有準確的定義,能夠參見一些資料,我本身描述下就是:在動態規劃求解過程當中的,子問題產生的解對於子問題來講也是一個最優解

多階段決策:一步步的決策,無後效性,決策只依賴於當前狀態,不依賴於以前的狀態。


看看象棋問題的最優子結構性質:在到達終點以前的任意(i , j)點所走過的方法數都是最少的。

多階段決策:每次決定往哪走只跟當前在哪有關,跟之前怎麼走的無關。


--------------------------------------------------------------------------------------------------------------------------------------------------


總結:

動態規劃的思想,理解深度,以上亮色字體標出部分!!!!


這篇信息量仍是很大的,要仔細理解,多看幾遍。算法

來源:http://www.cnblogs.com/kkgreen/archive/2011/06/26/2090702.html數組

相關文章
相關標籤/搜索