HDOJ2084 數塔------記憶化搜尋
Problem Description 在講述DP演算法的時候,一個經典的例子就是數塔問題,它是這樣描述的: 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; }