动态规划 数字三角形

数字三角形 Poj

问题描述

在这里插入图片描述
上面给出了一个数字三角形。从三角形的顶部到底部有多条不同的路径。对于每条路径,把路径上面的数字加起来可以得到一个和,累加和最大的路径成为“最佳路径”。题目的任务就是求出最佳路径上的数字之和。
注意:路径上的每一步只能从一个数走到下一层和它最近的左边数或者右边数。

输入数据

第一行是一个整数N,给出三角形的行数。下面的N行给出数字三角形。数字三角形中的数的范围都在0~100之间。

输出要求

输出最大的和。

输入样例

5
7
3 8
8 1 0
2 7 4 4
4 5 2 6 5

输出样例

30

递归思路

#include <iostream>
#include <algorithm>
#define MAX 101
using namespace std;
int D[MAX][MAX];
int n;
int MaxSum(int i, int j)
{
    if(i==n)
        return D[i][j];
    int x = MaxSum(i+1,j);
    int y = MaxSum(i+1,j+1);
    return max(x,y)+D[i][j];
}
int main(){
    int i,j;
    cin >> n;
    for(i=1;i<=n;i++)
        for(j=1;j<=i;j++)
            cin >> D[i][j];
    cout << MaxSum(1,1) << endl;
    return 0;
}

这道题如果用普通的递归来做,可以做出来,但时间复杂度很大,因为中间有很多重复计算,所以效率很低,如果算一个100行的数字三角形,那这个算出这个结果的时间将是不可估量的。
但可以考虑记忆化递归来避免重复计算。
每算出一个MaxSum(r,j)就保存起来,下次用到其值的时候直接取用,这样可免去重复计算。那么可以用O(n^2)时间完成计算。因为三角形的数字总 数是 n(n+1)/2

#include<iostream>
#include<algorithm>
using namespace std;
#define MAX 101
int D[MAX][MAX]; int n;
int maxSum[MAX][MAX];
int MaxSum(int i, int j){
    if( maxSum[i][j] != -1 )
        return maxSum[i][j];
    if(i==n)
        maxSum[i][j] = D[i][j];
    else {
        int x = MaxSum(i+1,j); int y = MaxSum(i+1,j+1); maxSum[i][j] = max(x,y)+
        D[i][j]; }
    return maxSum[i][j];
}

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

动归思路

"动态规划"是将一个问题分解为子问题递归求解,并且将中间结果保存以避免重复计算的方法。
而递归的思想在编程时未必要实现为递归函数,可以用递推的方法来解决。从maxSum[N-1]这一行元素开始向上逐行递推,最终得到maxSum[0][0]的值,即为最大值。

#include<iostream>
#include<algorithm>
using namespace std;
#define M 100
int d[M][M];
int maxSum[M][M];

int main()
{
    int n;
    cin>>n;
    for(int i=0;i<n;i++)
    {
        for(int j=0;j<=i;j++)
        {
            cin>>d[i][j];
        }
    }
    for(int i=0;i<n;i++)
    {
        maxSum[n-1][i]=d[n-1][i];
    }
    for(int i=n-1;i>0;--i)
    {
        for(int j=0;j<i;j++)
        {
            if(maxSum[i][j]>sumMax[i][j+1])
                maxSum[i-1][j]=sumMax[i][j]+d[i-1][j];
            else
                maxSum[i-1][j]=sumMax[i][j+1]+d[i-1][j];
        }
    }
    cout<<maxSum[0][0]<<endl;
    return 0;
}

还可以再进一步对空间做优化,在用递推求解过程中,maxSum[i][j]的值在用来计算出maxSum[i-1][j]的值后已经无用,所以可以将计算出的maxSum[i-1][j]的值直接存放在maxSum[i][j]的位置。这样maxSum不需要是二维数组,一维数组就足够了。

#include<iostream>
#include<algorithm>
using namespace std;
#define M 100
int d[M][M];
int *sumMax;
int main()
{
    int  n;
    cin>>n;
    for(int i=0;i<n;i++)
    {
        for(int j=0;j<=i;j++)
        {
            cin>>d[i][j];
        }
    }
    sumMax=d[n-1]; //sumMax指向最后一行
    for(int i=n-2;i>=0;i--)
    {
        for(int j=0;j<=i;j++)
        {
            sumMax[j]=max(sumMax[j],sumMax[j+1])+d[i][j];
        }
    }
    cout<<sumMax[0]<<endl;
    return 0;
}

但时间复杂度仍然是O(N^2)。

猜你喜欢

转载自blog.csdn.net/qq_41822647/article/details/85250085