动态规划之数字三角形

动态规划之数字三角形

问题描述

问题:
给定一个由n行数字组成的数字三角形,如下图所示:

试设计一个算法,计算出从三角形的顶至底的一条路径,使该路径经过的数字总和最大(每一步只能从一个数走到下一层上和它最近的左边的数或者右边的数)。

输入格式:
  第一行是数字三角形的行数,接下来 n 行是数字三角形中的数字。
输出格式:
  最大总和(整数)
样例输入
5
7
3 8
8 1 0
2 7 4 4
4 5 2 6 5
样例输出
30

解法一:普通递归

此解法的思想是从a[0][0]开始递归,len(a[i]][j]) = max(len(a[i+1][j]), len(a[i+1][j+1])) + a[i][j],递归到最底层之后开始对递归返回。
对于n层的数字三角形,该解法的时间复杂度为2*(1+2+4+8+16+….+2^(n-1)),因而为O(2*2^n-1)约等于O(2^n)

#include<iostream>
#define MAX 101

using namespace std;

int a[MAX][MAX];

int n;

int max_sum(int i, int j)
{
    int max1, max2;
    if(i == (n-1)) return a[i][j]; 
    max1 = max_sum(i+1, j);
    max2 = max_sum(i+1, j+1);
    if(max1>max2)
    {
        return max1+a[i][j];

    }
    else
    {
        return max2+a[i][j];    
    }
}

int main()
{   
    int i = 0, j = 0;
    cin>>n;
    for(i = 0; i<n; i++)
    {
        for(j = 0; j<=i; j++)
        {
            cin >> a[i][j];
        }
    }

    cout << max_sum(0, 0)<<endl;

    return 0;
}

解法二:记忆型递归

此解法的思想是再开辟一块内存来代替递归时的记忆过程,时间开销基本不变,加上空间消耗,总体性能不如解法一。

#include<iostream>
#define MAX 101

using namespace std;

int a[MAX][MAX];
int n;
int maxnum[MAX][MAX];

int max(int a, int b)
{
    return a>b ? a : b;
}

int max_sum(int i, int j)
{
    int max1, max2;
    if(i == (n-1)) 
    {
        maxnum[i][j] = a[i][j];
    }
    if(maxnum[i][j] != -1)
    {
        return maxnum[i][j];
    }
    max1 = max_sum(i+1, j);
    max2 = max_sum(i+1, j+1);
    maxnum[i][j] = max(max1, max2) + a[i][j];

    return maxnum[i][j];
}

int main()
{   
    int i = 0, j = 0;
    cin>>n;
    for(i = 0; i<n; i++)
    {
        for(j = 0; j<=i; j++)
        {
            cin >> a[i][j];
            maxnum[i][j] = -1;
        }
    }
    cout << max_sum(0, 0)<<endl;
    PrintArr();
    return 0;
}

解法三:递推型动规

与解法二相比,这种解法以递推取代递归,避免了进栈出栈的时间开销。对于有n层的数字三角形,时间复杂度为O(n^2/2),约等于O(n^2)。

#include<iostream>
#define MAX 101

using namespace std;

int  a[MAX][MAX];
int maxsum[MAX][MAX];
int n;

int max(int a, int b)
{
    return (a>b ? a : b);
}

int max_sum(void)
{
    int i = 0;
    int j = 0;
    int max1, max2;
    for(i = n-1; i>=0; i--)
    {
        for(j = 0; j<=i; j++)
        {
            if(i == (n-1))
            {
                maxsum[i][j] = a[i][j];
                continue;
            }
            max1 = maxsum[i+1][j];
            max2 = maxsum[i+1][j+1];
            maxsum[i][j] = max(max1, max2) + a[i][j];

        }
    }
    return maxsum[0][0];    
}

int main()
{
    int i = 0, j = 0;
    cin>>n;
    for(i = 0; i<n; i++)
    {
        for(j = 0; j<=i; j++)
        {
            cin >> a[i][j];
            maxsum[i][j] = -1;
        }
    }

    cout << max_sum()<<endl;

    return 0;
}

解法四:递推型动规空间优化

解法四是对解法三的一种优化,在开辟辅助空间时不再开辟一个n*n的二维数组,而是采用长度为n的一维数组。因为对数字三角形的计算结果是自底向上的,对每一层的结果在上一层利用完之后就没用了,并且每一层的大小从低往上是递减的,因此可以开辟最下层大小的空间来保存每一层的计算结果。当然,时间复杂度仍为解法三的时间复杂度。

#include<iostream>
#define MAX 101

using namespace std;

int a[MAX][MAX];
int maxsum[MAX];
int n; 

int max(int a, int b)
{
    return (a>b ? a : b);
}

int max_sum(void)
{
    int i = 0, j = 0;
    int max1, max2;
    for(i = n-1; i>=0; i--)
    {
        for(j = 0; j<=i; j++)
        {
            if(i == (n-1))
            {
                maxsum[j] = a[i][j];
                continue;
            }
            max1 = maxsum[j];
            max2 = maxsum[j+1];
            maxsum[j] = max(max1, max2) + a[i][j];

        }   
    }
    return maxsum[0];
}

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

    return 0;
}

解法五:递推型动规最终空间优化

在完成解法四的时候我们发现:既然每一层的结果在用完之后该层就没用了,那么我们完全没有必要开辟额外的辅助空间–利用二维数组a本身最下层的空间就好了。

#include<iostream>
#define MAX 101

using namespace std;

int a[MAX][MAX];
int n;

int max(int a, int b)
{
    return (a>b ? a : b);
}

int max_sum(void)
{
    int i = 0, j = 0;
    int max1 = 0, max2 = 0;
    for(i = (n-2); i>=0; i--)
    {
        for(j = 0; j<=i; j++)
        {
            max1 = a[i+1][j];
            max2 = a[i+1][j+1];
            a[i][j] += max(max1, max2);
        }
    }
    return a[0][0];
}

int main()
{
    int i = 0, j = 0;
    cin>>n;
    for(i = 0; i<n; i++)
    {
        for(j = 0; j<=i; j++)
        {
            cin >> a[i][j];
        }
    }
    cout << max_sum()<<endl;
    return 0;
}
  • 4
    点赞
  • 30
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值