动态规划三角形求和问题

文章讲述了如何使用动态规划方法解决一个计算n行数字三角形中从顶到底路径最大和的问题,重点介绍了自顶向下和自底向上的两种求解策略,以及状态转移方程dp[i][j]=max(dp[i-1][j-1],dp[i-1][j])+a[i][j]的应用。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

问题描述

7 
3 8 

8 1 0 

2 7 4 4 

4 5 2 6 5 

(图一) 

图一表示一个5行的数字三角形。假设给定一个n行数字三角形,计算出从三角形顶至底的一条路径,使该路径经过的数字总和最大。 
每一步只能由当前位置向左下或右下。

输入格式

你的程序要能接受标准输入。第一行包含一个整数T,表示总的测试次数。
对于每一种情况:第一行包含一个整数N,其中1 < N < 100,表示三角形的行数。
接下来的N行输入表示三角形的每一行的元素Ai,j,其中0 < Ai,j < 100。

输出格式

输出每次测试的最大值并且占一行。

输入样例

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

输出样例

30

详解

dp三步走:

①定义dp方程的含义 定义是dp的重点,但也有迹可循,一般是定义二阶的dp【i】【j】
此题dp方程的含义可以定义为走到第i行第j列时的最大和是多少

②找出dp方程(或称状态转移方程) 也就是找出递推式,我们先手写模拟解题的过程:
题目是说从上往下,但这样不好分析,因为从上往下分析,最下面有n个出口。
所以我们从下往上分析可以很明显的知道更新完整个矩阵之后,答案就是dp[0][0]
从题目倒数第二行开始,想一下2怎么更新?,是不是从它正下和右下选出最大数再加上他本身,所以2更新为 2 + 5 = 7.
那7怎么更新呢,是不是从它正下的5和右下的2选出最大数是5再加上它本身的7得到12
选最大值这个动作怎么用代码表示?可以抽象为max(正下的数,右下的数) 所以手推模拟后发现dp方程是dp[i][j] = max( dp
[i+1] [j] , dp [i+1] [j+1]) (0 <= i <= n -1,0 <= j <=
i)(i表示行,j表示列,都是从0开始) 有很多人发现了我们没有从最后一行开始往上推,而是从倒数第二行往上推的
这就涉及到我们下面要讲的初始边界确定

③确定初始边界状态 因为最后一行下面没有路了,所以最后一行的值不用更新就是他们本身, 则初始状态就是当i = n - 1时,a[i][j]
= 他们本身(i表示行,j表示列,都是从0开始)

自顶向下求解

网上大部分代码都是从最后一层向上动态规划求解,对于初学者可能不太友好,这里给出一种从上往下的动态求解方式。(从底向上代码请往下看。)

#include<iostream>
using namespace std;
#include<bits/stdc++.h>


int main()
{
	int t;
	cin>>t;
	while(t--)
	{
		int n;
		cin>>n;
		int a[n][n];
		for(int i=0;i<n;i++)
		{
			for(int j=0;j<=i;j++)
			cin>>a[i][j];
		}
		int dp[n][n];   //动态规划三角形,每个位置代表之前的最大和
		memset(dp,0,sizeof(dp));
		dp[0][0]=a[0][0];  //最顶部和就是三角形第一个数
		for(int i=1;i<n;i++)
		{
			for(int j=0;j<=i;j++)
			{
				dp[i][j]=max(dp[i-1][j-1],dp[i-1][j])+a[i][j];   //求和
			}
		}
		//所有的通路之和都在最后一层,现在遍历找最大值
		int ma=0;  
		for(int i=0;i<n;i++)
		{

		     ma=max(dp[n-1][i],ma);
		}
		cout<<ma<<endl;
	}

	return 0;
}

自底向上求解

#include<iostream>
#include<algorithm>
using namespace std;
int dp[110][110];
int main(){
    int T,n;
    cin >> T;
    while(T--){
        cin >> n;
        //输入部分
        for(int i = 0;i < n;i++)
            for(int j = 0;j <= i;j++)
                cin >> dp[i][j];
        //输入部分
        for(int i = n - 2;i >= 0;i--)
            //从倒数第二行开始往第一行更新值
            for(int j = 0;j <= i;j++)
                //当前行值不得超过列值
                dp[i][j] += max(dp[i+1][j],dp[i+1][j+1]);
                //当前的值等于正下(dp[i+1][j])和右下(dp[i+1][j+1])的最大数加上他本身
        cout << dp[0][0] << endl;
        //输出结果
    }
    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值