HDOJ2084 数塔------记忆化搜索

题目链接 http://acm.hdu.edu.cn/showproblem.php?pid=2084

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;
}

 

 

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值