数字三角形

问题描述与状态定义

    有一个由非负整数组成的三角形,第一行只有一个数字,出了最下行之外每个数的左下方和右下方各有一个数,如下,从第一行的数开始,每次可以往左下或右下走一格,直到走到最下行,把沿途经过的数加起来,如何使和最大?


状态:把当前的位置(i,j)看成一格状态,然后定义状态(i,j)的指标函数d(i,j)为格子(i,j)出发时能得到的最大和(包括格子(i,j)本身的值)。在这个状态定义下,原问题的解是d(1,1)。

易知状态转移方程:d(i,j)=a(i,j)+max{d(i+1,j),d(i+1,j+1)}

状态和状态转移方程一起完整的描述了具体的算法,二者是动态规划的核心

方法一、递归计算(时间复杂度O(2^n))

思路简单明了,但是效率十分低下,由于相同子问题被重复计算了多次。如果有n个结点,则调用关系树也会有n层,一共有2^n-1个结点


#include<iostream>
#include<algorithm>
using namespace std;
const int N=51;
int a[N][N];
int n;
int d(int i, int j)
{
	if(i == n)
		return a[i][j] + 0;
	else
		return a[i][j] + max(d(i+1,j),d(i+1,j+1));
}
int main()
{
	int i, j;
	cin>>n;
	for(i=1; i<=n; i++)
		for(j=1; j<=i; j++)
			cin>>a[i][j];
	cout<<d(1,1)<<endl;
	return 0;
}

方法二、递推计算(时间复杂度O(n^2))

#include<iostream>
#include<algorithm>
using namespace std;
const int N=51;
int a[N][N];
int d[N][N];
int n;
int main()
{
	int i, j;
	cin>>n;
	for(i=1; i<=n; i++)
		for(j=1; j<=i; j++)
			cin>>a[i][j];
	for(j=1; j<=n; j++)
		d[n][j] = a[n][j];//边界处理(数字三角形最后一行的数字)
	for(i=n-1; i>=1; i--)//在计算d[i][j]之前,它所需要的d[i+1][j]和d[i+1][j+1]
		for(j=1; j<=i; j++)//一定已经计算出来了。避免了重复计算
			d[i][j] = a[i][j] +max(d[i+1][j],d[i+1][j+1]);//注意计算顺序为逆序
	cout<<d[1][1]<<endl;
	return 0;
}

方法三、记忆化搜索(时间复杂度O(n^2))

#include<iostream>
#include<cstring>
#include<algorithm>
using namespace std;
const int N=51;
int a[N][N];
int d[N][N];
int n;
int solve(int i, int j)
{
	if(d[i][j] >= 0)
		return d[i][j];
	else
		if(i == n)
			return d[i][j] = a[i][j] + 0;
		else
			return d[i][j] = a[i][j] + max(solve(i+1,j),solve(i+1,j+1));
}
int main()
{
	int i, j;
	memset(d,-1,sizeof(d));//把d全部初始化为-1,可通过判断d[i][j]>=0得知它是否被计算过
	cin>>n;
	for(i=1; i<=n; i++)
		for(j=1; j<=i; j++)
			cin>>a[i][j];
	cout<<solve(1,1)<<endl;
	return 0;
}





  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值