其實動態規劃本身並不是乙個特定的演算法,是一種用途廣泛的問題求解方法,一種思想,一種手段。
有乙個有非負整數組成的三角形,第一行乙個數字,下面各行除了最後一行外,每行的每個數字下面左右各乙個數字。
如圖示:
從第一行數字開始,每次只能走左下或右下一格,直到走到最後一行,把沿途的走過的所有數字加起來。
如何能使這個和最大?
【問題複雜度分析】如果熟悉回溯法,就會立即發現這是乙個動態的決策問題:每次兩個選擇----左下或右下。
但是如果選擇用回溯法解決此問題,慣常的問題就是效率太低:一格n層的數字三角形的完整路線有2^n條,所以當n很大時完全不能靠此方法。
因此為了提高效率,需要把此問題抽象:把所有的位置(i,j)抽象為乙個個不同的狀態,然後定義狀態(i,j)的指標函式d(i,j)為從格仔(i,j)出發時能得到的最大的和(包括此格仔本身)。在這個定義下,原問題的解是d(1,1)。
如下圖:
【狀態轉移分析】從格仔(i,j)出發有兩種決策。若往左走,則走到(i+1,j)後續要求「從(i+1,j)出發後能得到的最大和「這一問題,即是d(i+1,j)。類似的,往右做之後要求解d(i,j+1)。由於這兩個選擇自由可選,所以,應該選擇較大的。即得到了所謂的狀態轉移方程:
d(i,j) = a(i,j) + max
最優子結構:如果往左走,最好的情況是(i,j)格仔裡面的值a(i,j) 與」從(i+1,j)出發後能得到的最大和"之和, 注意"最大"兩個字,如果從(i,j)出發到底部這部分都不是最大的話,加上a(i,j)也必然不是最大的。這就是最優子結構。或描述全域性最優解包含區域性最優解。
【總結】動態規劃的核心是狀態和狀態轉移方程。
**如下:(注意邊界)
int d(int i, int j)
如此計算正確只是效率依然不高,問題在於重複計算。就是一些格仔被兩個父節點所共有,所以,在遞迴的時候,便會被重複計算。**如下:
(注意此時重複邊界的處理)
int i, j;
for (j=1; j<=n; j++)
d[n][j] = a[n][j] ;
for (i=n-1; i>=1; i--)
for (j=1; j<=i; j++)
時間複雜度顯然是o(n^2),
可以如此計算的原因在於:i是逆序列舉的,因此在計算d[i] [j] 之前,他所需要的d[i+1][j]
和 d[i][j+1] 已經計算出來了。
程式分為兩部分。首先用陣列函式memset();將陣列整體置為-1,然後寫遞迴函式:
int d(int i, int j)
依然是遞迴函式,同時把計算結果存在陣列d中。題目說各個數字均為非負數,因此如已經計算過某個d[i][j],那麼期應該是非負數。這樣只需要把所有的陣列元素初始化為負數,如-1,就可以知道是否計算過d[i][j].
注意:不要忘記把結果存在陣列d[i][j]中。根絕c語言的」賦值語句本身有返回值「的規定,可以把儲存d[i][j]的工作整合在函式的返回語句中。
上述的方法三稱為記憶化,雖然不像地推算法那樣顯示的指明了計算的順序,但是任然可以保證每個節點只訪問了一次。
由於i和j都在1~n之間,所以所有的不同的節點之間一共有o(n^2)個。即是時間複雜度。
hdoj--2084
未完待續...................
DP入門 動態規劃初步 幾類子串行問題
動態規劃初步 子串行問題,本文包括 最大連續子串行和,最大 長 上公升子串行,最大 長 公共子串行 找出一維陣列中和最大的連續子串行,狀態轉移方程如下 su m ma x a i s um,a i 例題 hdu 1003 max sum 最大連續子串行和 注 此題由於要輸出和最大序列的起始位置和終止...
演算法競賽入門(1)STL初步
sort 如希望使用sort排序,應該將待排列型別定義 小於 運算子 或在排序時插入乙個 小於 函式。排序物件可以放在普通陣列中,也可以放在vector中。前者用sort a,a n 後者用sort v.begin v.end 呼叫。lower bound作用是查詢 大於等於x的第乙個位置 示例 i...
wireshark實驗一 初步入門
一,實驗目的 1,了解的wireshark的功能原理 2,安裝wireshark的軟體 3,了解的wireshark軟體介面功能 二,實驗環境 1,聯網的pc機,作業系統windows7的及以上 三,實驗內容 1,wireshark的介紹 上圖是資料報嗅探器的結構,最右側為計算機上執行的協議與網路應...