萌新做点小玩意儿DAY-8 逐步优化数字三角形算法

前两天的回溯法的问题都是使用递归解决的,但是有的时候问题规模比较大的时候递归回溯的时间复杂度过高,以指数的形式的增长,例如今天的数字三角形的问题。所以如何优化递归使之转化为递推迭代就至为重要。

问题描述:在类似于下图中的数字三角形中寻找一条从顶部到底部的路径,使得路径上经过的数字之和最大。路径上每一步只能往左下或者右下走。求出这个最大的和,其中三角形的行数大于1小于100,数字为0-99。

递归思想:用二维数组存放数字三角形,n表示三角形行数,x[i][j]表示第i行第j个数字,Maxsum(i,j)表示从x[i][j]到底边的最佳路径的数字和,求Maxsum(1,1)即可以解决问题。因为从x[i][j]出发,下一步只能走x[i+1][j]或者x[i+1][j+1]。对于N行的三角形我们不难写出递归函数:

int MaxSum(int i, int j){
	if(i == n)
		return x[i][j];
	int x = Maxsum(i+1, j);
	int y = Maxsum(i+1, j+1);
	return max(x, y)+x[i][j];
}

然后再从main()函数中输入三角形输出Maxsum(1,1)的值即可,但是这个算法的时间复杂度到了O(2^n)(以图中三角形为例,第一二行的数字只需要求一次Maxsum,而第三行的1要求两次,因为3和8两条路径,同样的第四行的7和4要求3次,以此类推总次数和n的关系为2^n-1次),当n到100的时候这辈子计算机也算不出来的。

一次改善:新建一个数组maxsum[][]用于存放每一个计算出的Maxsum(i,j)的值时间复杂度可以减少到O(n^2),可以改进Maxsum递归函数:

int MaxSum(int i, int j){
	if(maxsum[i][j] != -1)
		return maxsum[i][j];
	if(i == n)
		maxsum[i][j] = x[i][j];
	int x = Maxsum(i+1, j);
	int y = Maxsum(i+1, j+1);
	maxsum[i][j] = max(x, y)+x[i][j];
	return maxsum[i][j];
}

在main函数给x[i][j]赋值的同时,对maxsum[i][j]进行初始化赋值maxsum[i][j] = -1。最后输出MaxSum(1,1)的值即可。

for(i = 1; i <= n; i++)
	for(j = 1; j <= i; j++){
		cin >>x[i][j];
	        maxsum[i][j] = -1;
	}

二次改善:变递归为递推,自底向上进行递推,第n行的数字的maxsum保持不变,从第n-1行依次向上递推选择底边相对大的数作为新的maxsum从而构建一个与三角形相对应的maxsum的数组。如图所示:

部分代码如下:

int main(){
	int i, j;
	cin >>n;
	for(i = 1; i <= n; i++)
		for(j = 1; j <= i; j++)
			cin >>x[i][j];
	for(i = 1; i <= n; ++i)
                maxsum[n][i] = x[n][i];
        for(i = n-1; i >= 1; --i)
		for(j = 1; j <= i; ++j)
			maxsum[i][j] = max(maxsum[i+1][j],maxsum[i+1][j+1])+x[i][j];
	cout <<maxsum[1][1]<<endl;
	return 0;
}

这样可以把递归转化为递推提高代码的运行效率,但是还存在一定的问题,整个maxsum数组的利用率很低,造成了储存空间上的浪费。图中可以很清楚的看出来有1/2*n(n-1)的数组空间被浪费了。所以可以不用二维数组来表示最大和。

三次改善:选用一维数组maxsum[],不断更新maxsum中的值,maxsum长度仅仅为n,进而不难相出甚至可以不用maxsum数组,只需要定义一个指针来指向x[n]就好。完全代码如下:

#include <iostream>
#include <algorithm>
using namespace std;
#define MAX 101
int x[MAX][MAX];
int * maxsum;
int n;

int main(){
	int i, j;
	cin >>n;
	for(i = 1; i <= n; i++)
		for(j = 1; j <= i; j++)
			cin >>x[i][j];
	maxsum = x[n];
	for(i = n-1; i >= 1; --i)
		for(j = 1; j <= i; ++j)
			maxsum[j] = max(maxsum[j],maxsum[j+1])+x[i][j];
	cout <<maxsum[1]<<endl;
	return 0;
}

代码运行测试图如下:

总而言之,学习一个好的时空均衡的算法的过程都是这样一次又一次的把算法逐步改进的。换一种思维来考虑某个题的解题过程也许会有很大的收获。

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值