题目链接 http://acm.hdu.edu.cn/showproblem.php?pid=2084
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;
}