数塔问题(动态规划)

问题描述

观察下面的数字金字塔。

写一个程序来查找从最高点到底部任意处结束的路径,使路径经过数字的和最大。每一步可以走到左下方的点也可以到达右下方的点。

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

在上面的样例中,路径 7→3→8→7→5 的数字和最大

输入格式

第一个行一个正整数 N,表示行的数目。

后面每行为这个数字金字塔特定行包含的整数。

输出格式

单独的一行,包含那个可能得到的最大的和。

输入输出样例

输入:

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

输出:

30

数据范围

对于100%的数据,1 ≤ N≤ 1000,所有输入在 [0,100] 范围内。


思路分析:

1.数塔用二维数组来存储,定义dp[1000][1000],前一个对应数塔的行数,后一个对应数塔中每一行的个数;

2.找最大数的路径(递归:从上至下)

两层数塔,相要找到它的最大和,只要比较1下面一层的两个数谁最大相加即可;

即dp[0][0]+max(dp[1][0]+dp[1][1])

1
23

三层数塔:用dfs(0,0)表示该数找到的最大路径

对于下面的数塔就可以转化为的子问题即为:dfs(0,0)=dp[0][0]+max(dfs(1,0)+dfs(1,1));

dfs(1,0)=dp[1][0]+max(dp[2][0]+dp[2][1]);dfs(1,1)=dp[1][1]+max(dp[2][1]+dp[2][2])

1
23
456

......以此类推

·数塔中的一个数和它下面对应的两个数在二维数组的下标关系:dp[i][j]

左下方:dp[i+1][j];右下方:dp[i+1][j+1]

实现代码:

#include<iostream>
using namespace std;

int dp[1000][1000];
int N;

int dfs(int i,int j)
{
    if(i==N-1)
        return dp[i][j];//递归边界即最后一行直接返回
    else
        return dp[i][j]+max(dfs(i+1,j),dfs(i+1,j+1));
}

int main(void)
{
    scanf("%d",&N);//运行速度比cin快
    for(int i=0;i<N;i++)
    {
        for(int j=0;j<=i;j++)
            scanf("%d",&dp[i][j]);
    }
    int max=dfs(0,0);
    cout<<max;
    return 0;
}

用递归后,容易发现如果数据量过大,重复的计算太多,就需要大量的时间,这就很容易超时,所以可以用动态规划(从下到上的解决问题)

3.动态规划的基本思想:将待求解问题分解成若干个子问题,先求解子问题,然后从这些子问题的解得到原问题的解。我们需要保存已解决的子问题的答案,这样可以避免大量的重复计算,节省时间。我们会用一个表来记录所有已解的子问题的答案。不管该子问题以后是否被用到,只要它被计算过,就将其结果填入表中,而在需要时再找出已求得的答案。
 

#include <iostream>
using namespace std;
int main(void)
{
	int dp[1000][1000];
	int f[1000][1000]={0};
	int N;
	scanf("%d",&N);
    
    for (int i = 0;i <= N;i++)
    {
    	for (int j = 0;j <= i;j++)
            scanf("%d",&dp[i][j]);
	}
                
    for (int i = N-1;i >= 0;i--)//用i来表示行,且从最后一行开始
    {
    	for (int j = 0;j <= i;j++)//每行有i列,且从第一列开始
            f[i][j] = max(f[i + 1][j],f[i + 1][j + 1]) + dp[i][j];//自底向上计算最长路径
	}           
    cout << f[0][0];//当for循环结束完毕之后f[0][0]:从数塔第一层开始的最长路径
    return 0;
}

评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值