蓝桥杯数字三角形

数字三角形(进阶版)

问题描述
  (图3.1-1)示出了一个数字三角形。 请编一个程序计算从顶至底的某处的一条路
  径,使该路径所经过的数字的总和最大。
  ●每一步可沿左斜线向下或右斜线向下走;
  ●1<三角形行数≤100;
  ●三角形中的数字为整数0,1,…99;
在这里插入图片描述
文件中首先读到的是三角形的行数。
接下来描述整个三角形
输出格式:
最大总和(整数)
样例输入
5
7
3 8
8 1 0
2 7 4 4
4 5 2 6 5
样例输出
30
1、首先我们考虑能否使用搜索去做,因为每一步可沿左斜线向下或右斜线向下走,也就是说我们到达了一个位置(i,j),接着可以到达(i+1,j)或者(i+1,j+1);那么根据这个规律,就可以进行递归搜索,代码如下:

#include <cstdio>
#include <iostream>
#include <cstring>
#include <string>
#include <algorithm>
#include <math.h>
using namespace std;
int val[101][101];
int n,ans,cnt;//ans位搜索的最大值,cnt为当前最大值
void  dfs(int i,int j)//从(i,j)开始向下搜索
{
	if(i==n+1)//最后一行,终止条件
	{
		if(cnt>ans)
		{
			ans=cnt;
		}
		return;
	}
	cnt+=val[i][j];//加上当前位置的数值
	dfs(i+1,j);
	dfs(i+1,j+1);
	cnt-=val[i][j];
}
int main()
{
	cin>>n;
	for(int i=1;i<=n;i++)
	{
		for(int j=1;j<=i;j++)
		{
			cin>>val[i][j];
		}
	} 
	dfs(1,1);
	cout<<ans;
    return 0;
}

复杂度很明显是2的n次方,时间很容易超限
原因分析如下:
当我们要计算(i,j)这个位置时,这个位置是由(i-1,j)转移过来的,我们已经求出了所有的(i.j)下面的位置,我们向上返回到(i-1,j)这个位置,我们向下就到达了(i,j+1)。我们发现(i,j)可以到达(i+1,j+1)并且(i,j+1)也可以到达(i+1,j+1)。那么(i+1,j+1)这个点我们就计算了两次,会导致大量的重复计算,效率低下,导致时间超限。因此我们考虑,如果有一个数组用过来保存(i,j)能够得到的最大值,那这样我们就不用重复计算,那么就是把整个数组遍历一遍,时间复杂度就是等差数组(n+1)*n/2,这样时间复杂度就大大降低了。我们把这个方法叫做记忆化,这样整个方法就叫做记忆化搜索。

#include <cstdio>
#include <iostream>
#include <cstring>
#include <string>
#include <algorithm>
#include <math.h>
using namespace std;
int val[101][101];
int f[101][101]; 
int n,ans,cnt;
int  dfs(int i,int j)
{
	if(f[i][j]!=-1) 
	{
		return f[i][j];
	}
	if(i==n+1)
	{
		return f[i][j]=val[i][j];
	}
	return f[i][j]=max(dfs(i+1,j+1),dfs(i+1,j))+val[i][j];
}
int main()
{
	memset(f,-1,sizeof(f));//初始化数组f 
	cin>>n;
	for(int i=1;i<=n;i++)
	{
		for(int j=1;j<=i;j++)
		{
			cin>>val[i][j];
		}
	}
	cout<<dfs(1,1);
    return 0;
}

接着优化,因为递推要比递归快得多,因此平时我们要尽量将递归改造为递推,我们之前f[i][j]表示的是从(i,j)出发能够得到的最大的值,现在改造一下,将f[i][j]表示为从(1,1)出发到(i,j)能够得到的最大的值。而这样就能使用递推,因为f[i][j] 可以由f[i-1][j-1] 和f[i-1][j]得到,而这就是一个递推式,因此当我们计算(i,j)的时候只要保证(i-1,j-1)和(i-1,j)已经计算并且保存就行。而这种形式我们采用的是从上到下的顺推形式。代码如下:

#include <cstdio>
#include <iostream>
#include <cstring>
#include <string>
#include <algorithm>
#include <math.h>
using namespace std;
int val[101][101];
int f[101][101]; 
int n,ans;
int main()
{
	memset(f,0,sizeof(f));//初始化数组f 
	cin>>n;
	for(int i=1;i<=n;i++)
	{
		for(int j=1;j<=i;j++)
		{
			cin>>val[i][j];
		}
	} 
	for(int i=1;i<=n;i++)
	{
		for(int j=1;j<=i;j++)
		{
			if(j==1)
			{
				f[i][j]=val[i][j]+f[i-1][j];
			}
			else if(j==n)
			{
				f[i][j]=val[i][j]+f[i-1][j-1];
			}
			else f[i][j]=max(f[i-1][j-1],f[i-1][j])+val[i][j];
		}
	}
	for(int i=1;i<=n;i++)
	{
		ans=max(ans,f[n][i]);
	}
	cout<<ans;
    return 0;
	
}

接着我们继续优化如果我们从上到下求最大值,转化为从最后一排出发到达最后一层的最大值,那么对于(i,j)来说,可以从(i+1,j+1) (i+1,j)得到,那么我们就可以将整个递推倒过来写,省区最后的一个for循环

#include <cstdio>
#include <iostream>
#include <cstring>
#include <string>
#include <algorithm>
#include <math.h>
using namespace std;
int val[101][101];
int f[101][101]; 
int n;
int main()
{
	memset(f,0,sizeof(f));
	cin>>n;
	for(int i=1;i<=n;i++)
	{
		for(int j=1;j<=i;j++)
		{
			cin>>val[i][j];
		}
	} 
	for(int i=n;i>=1;i--)
	{
		for(int j=1;j<=i;j++)
		{
			f[i][j]=max(f[i+1][j+1],f[i+1][j])+val[i][j];
		}
	}
	cout<<f[1][1];
    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值