1. 程式人生 > >HDOJ2084 數塔------記憶化搜尋

HDOJ2084 數塔------記憶化搜尋

Problem Description

在講述DP演算法的時候,一個經典的例子就是數塔問題,它是這樣描述的:

有如下所示的數塔,要求從頂層走到底層,若每一步只能走到相鄰的結點,則經過的結點的數字之和最大是多少?

已經告訴你了,這是個DP的題目,你能AC嗎?

 

Input

輸入資料首先包括一個整數C,表示測試例項的個數,每個測試例項的第一行是一個整數N(1 <= N <= 100),表示數塔的高度,接下來用N行數字表示數塔,其中第i行有個i個整數,且所有的整數均在區間[0,99]內。

 

Output

對於每個測試例項,輸出可能得到的最大和,每個例項的輸出佔一行。

 

Sample Input

1 5 7 3 8 8 1 0 2 7 4 4 4 5 2 6 5

 

Sample Output

30

 思路: 

 設數塔三角形每個數字處座標為(row,col)(row行col列,從0開始),max(i,j)表示將i行j列的元素當作塔頂,從上到小連一條線的最大和,則題目要求求出max(0,0)。

轉移方程: max(i,j) = Max( max(i+1,j),max(i+1,j+1) ) + num(i,j)(i行j列處的數字),即max(i,j)等於(i,j)的數字加上max(i+1,j)和max(i+1,j+1)二者的最大值。

利用這個方程直接寫一個遞迴函式就可以了,但是這樣會造成很多重複計算,比如想計算max(0,0),需要max(1,0),max(1,1)的值,然後程式就會遞迴去計算max(1,0),這時又需要去遞迴算max(2,0)和max(2,1)....最後才倒退算出max(1,0).  之後又計算max(1,1),這時需要計算max(2,1)和max(2,2),但是max(2,1)前面已經計算過了,會重複計算。。

為了減少時間複雜度,減少大量無意義的重複計算,用dp陣列記錄下每次遞迴算出的中間值即可。

#include <cstdio>
#include<algorithm>
#include <cstring>
#define MAXN 105
using namespace std;

int dp[MAXN][MAXN];					// dp陣列 
int num[MAXN][MAXN];
int n;		// 塔高 

int Cal(int row,int col) {
	// 計算max(row,col),題目要求計算max(0,0)
	
	// 如果已經計算出結果直接返回 
	if (dp[row][col] != -1) 
		return dp[row][col];
	// 最底層 
	if(row == n-1)	
		return num[row][col];
	// 處在中間層
	int l = dp[row+1][col] = Cal(row+1,col);		// 左下方sum
	int r = dp[row+1][col+1] = Cal(row+1,col+1);	// 右下方sum
	 				
	return max(l,r) + num[row][col];	
}


int main(){
	int c;	
	scanf("%d",&c);	
	while(c--) {
		scanf("%d",&n);								
		for(int i = 0;i < n;i++) 			
			for(int j = 0;j <= i;j++) {
				scanf("%d",&num[i][j]);		
				dp[i][j] = -1; 			// 初始化陣列						
			}		
		for(int i = 0;i <= n-1;i++)
			dp[n-1][i] = num[n-1][i];			// 最底層的max為自身 
		
		printf("%d\n",Cal(0,0));
	}
	
	return 0;
}