數字三角形(POJ1163)php
Descriptionios
7
3 8
8 1 0
2 7 4 4
4 5 2 6 5
在上面的數字三角形中尋找一條從頂部到底邊的路徑,使得
路徑上所通過的數字之和最大。路徑上的每一步都只能往左下或
右下走。只須要求出這個最大和便可,沒必要給出具體路徑。
三角形的行數大於1小於等於100,數字爲 0 - 99數組
輸入格式:
5 //三角形行數。下面是三角形
7
3 8
8 1 0
2 7 4 4
4 5 2 6 5app
要求輸出最大和函數
Sample Output優化
30
Sourcespa
解題思路:
用二維數組存放數字三角形。
D( r, j) : 第r行第 j 個數字(r,j從1開始算)
MaxSum(r, j) : 從D(r,j)到底邊的各條路徑中,
最佳路徑的數字之和。
問題:求 MaxSum(1,1)
典型的遞歸問題。
D(r, j)出發,下一步只能走D(r+1,j)或者D(r+1, j+1)。故對於N行的三角形:
if ( r == N)
MaxSum(r,j) = D(r,j)
else
MaxSum( r, j) = Max{ MaxSum(r+1,j), MaxSum(r+1,j+1) } + D(r,j)code
改進blog
若是每算出一個MaxSum(r,j)就保存起來,下次用
到其值的時候直接取用,則可免去重複計算。遞歸
遞歸轉成遞推
「人人爲我」遞推型動歸 Pascal代碼
1 //By LYLtim 2 //2010.10.18 3 uses math; 4 var n,i,j:byte; 5 a:array[1..10,1..10]of word; 6 f:array[1..10,1..10]of word; 7 begin 8 assign(input,'tower.in');reset(input); 9 assign(output,'tower.out');rewrite(output); 10 readln(n); 11 for i:=1 to n do 12 begin 13 for j:=1 to i do 14 read(a[i,j]); 15 readln; 16 end; 17 fillchar(f,sizeof(f),0); 18 for i:=1 to n do f[n,i]:=a[n,i]; 19 for i:=n-1 downto 1 do 20 for j:=1 to i do 21 f[i,j]:=max(f[i+1,j],f[i+1,j+1])+a[i,j]; 22 writeln('max=',f[1,1]); 23 close(input);close(output); 24 end.
空間優化
不必用二維maxSum數組存儲每個MaxSum(r,j),只要從底層一行行向上
遞推,那麼只要一維數組maxSum[100]便可,即只要存儲一行的MaxSum值就
能夠。
進一步考慮,連maxSum數組均可以不要,直接用D的
第n行替代maxSum便可。
節省空間,時間複雜度不變
1 //By LYLtim 2 //2015.2.11 3 #include <iostream> 4 #include <algorithm> 5 using namespace std; 6 int main() 7 { 8 int n, d[101][101]; 9 cin >> n; 10 for (int i = 1; i <= n; i++) 11 for (int j = 1; j <= i; j++) 12 cin >> d[i][j]; 13 for (int i = n-1; i >= 1 ; i--) 14 for (int j = 1; j <= i; j++) 15 d[n][j] = max(d[n][j], d[n][j+1]) + d[i][j]; 16 cout << d[n][1]; 17 }
遞歸函數有n個參數,就定義一個n維的數組,數組
的下標是遞歸函數參數的取值範圍,數組元素的值
是遞歸函數的返回值,這樣就能夠從邊界值開始,
逐步填充數組,至關於計算遞歸函數值的逆過程。
動規解題的通常思路
1. 將原問題分解爲子問題
把原問題分解爲若干個子問題,子問題和原問題形式相同
或相似,只不過規模變小了。子問題都解決,原問題即解
決(數字三角形例)。
子問題的解一旦求出就會被保存,因此每一個子問題只需求
解一次。
2. 肯定狀態
在用動態規劃解題時,咱們每每將和子問題相
關的各個變量的一組取值,稱之爲一個「狀
態」。一個「狀態」對應於一個或多個子問題,
所謂某個「狀態」下的「值」,就是這個「狀
態」所對應的子問題的解。
全部「狀態」的集合,構成問題的「狀態空間」。「狀態
空間」的大小,與用動態規劃解決問題的時間複雜度直接相關。
在數字三角形的例子裏,一共有N×(N+1)/2個數字,因此這個
問題的狀態空間裏一共就有N×(N+1)/2個狀態。
整個問題的時間複雜度是狀態數目乘以計算每一個狀態所需
時間。
在數字三角形裏每一個「狀態」只須要通過一次,且在每一個
狀態上做計算所花的時間都是和N無關的常數。
用動態規劃解題,常常碰到的狀況是,K個整型變量能
構成一個狀態(如數字三角形中的行號和列號這兩個變量
構成「狀態」)。若是這K個整型變量的取值範圍分別是
N1, N2, ……Nk,那麼,咱們就能夠用一個K維的數組
array[N1] [N2]……[Nk]來存儲各個狀態的「值」。這個
「值」未必就是一個整數或浮點數,多是須要一個結構
才能表示的,那麼array就能夠是一個結構數組。一個
「狀態」下的「值」一般會是一個或多個子問題的解。
3. 肯定一些初始狀態(邊界狀態)的值
以「數字三角形」爲例,初始狀態就是底邊數字,值
就是底邊數字值。
4. 肯定狀態轉移方程
定義出什麼是「狀態」,以及在該 「狀態」下的「值」後,就要
找出不一樣的狀態之間如何遷移――即如何從一個或多個「值」已知的
「狀態」,求出另外一個「狀態」的「值」(「人人爲我」遞推型)。狀
態的遷移能夠用遞推公式表示,此遞推公式也可被稱做「狀態轉移方
程」。
能用動規解決的問題的特色1) 問題具備最優子結構性質。若是問題的最優解所包含的子問題的解也是最優的,咱們就稱該問題具備最優子結構性質。2) 無後效性。當前的若干個狀態值一旦肯定,則此後過程的演變就只和這若干個狀態的值有關,和以前是採起哪種手段或通過哪條路徑演變到當前的這若干個狀態,沒有關係。