数字三角形题解

Number Triangles

洛谷P1216

题目简化

观察下面的数字金字塔。

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

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

输入格式

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

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

输出格式

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

提示

【数据范围】
对于 100 % 100\% 100% 的数据, 1 ≤ r ≤ 1000 1\le r \le 1000 1r1000,所有输入在 [ 0 , 100 ] [0,100] [0,100] 范围内。

思路

u n k n o w n   p t s unknown~ pts unknown pts

没头没脑的穷举,我也不知道你能骗多少分.

55 p t s 55pts 55pts

这是一个动态决策题,每次都有两种选择,即

Alt text

明显可以用回溯dfs搜索,那么如果有一个有 n n n层的数字三角形,那么完整路径一共 2 n − 1 2^n-1 2n1条,肯定超时.

55 p t s 55pts 55pts

虽然也是55,但好像思维难度提高了

更高效的想法是dp.

不妨推样例:

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

举一个例子,我们求到7的最大路径.
不难想到,我们算到38的最大路径.
由这个思路我们继续往下想:

7-8...
|
3-1...
|
8-7...
|
2-5?
|
4?

那到4的最大路径呢?很简单就是4;
那么一步步往上回溯.
取到(左儿子的最大路径与到右儿子的最大路径)的最大值.
Alt text

我们将坐标铺设进三角形:7(1,1),3(2,1),8(2,2),8(3,1)…

∴ d p 5 , 1 = 4 , d p 5 , 2 = 5 , d p 4 , 1 = max ⁡ d p 5 , 1 , d p 5 , 2 ∴dp_{5,1}=4,dp_{5,2}=5,dp_{4,1}=\max{dp_{5,1}},{dp_{5,2}} dp5,1=4,dp5,2=5,dp4,1=maxdp5,1,dp5,2

也就是说
d p i , j = a i , j + max ⁡ d p ( i + 1 , j ) , d p ( i + 1 , j + 1 ) dp_{i,j}=a_{i,j}+\max{dp_{(i+1,j)}},{dp_{(i+1,j+1)}} dpi,j=ai,j+maxdp(i+1,j),dp(i+1,j+1)
这样我们就得到了

状态转移方程.

我们不难写出代码:

int solve(int i,int j){
	int a1,a2;
	if(i<n){
		a1=solve(i+1,j),a2=solve(i+1,j+1);
		return a[i][j]+max(a1,a2);
	}
	else return a[i][j];
}

完整

你也看到了,竟然T了,跟回溯没区别.qwq.

time: O ( 2 n ) O(2^n) O(2n)

memory: O ( n ) O(n) O(n)


100 p t s 100pts 100pts

失败是成功之母,TLE是WA之母,WA是AC之母.
不要TwT,我们其实快A了!

我们知道递归总会溢出,那我们尽量减少动规次数.

其实这个程序有重复计算!

Alt text

∴我们应该把 d p ( i , j ) dp_{(i,j)} dp(i,j)登记下来.

这叫做

记忆化搜索

#include <bits/stdc++.h>

using namespace std;

int d[1024][1024],a[1024][1024],n;

void input(){ //读入
	int i,j;
	for(i=1;i<=n;i++)
		for(j=1;j<=i;j++)
			cin>>a[i][j];
}

int dp(int i,int j){ //记忆化搜索
    if(d[i][j]>=0)
        return d[i][j]; //边界
    return d[i][j]=a[i][j]+(i==n ? 0 :
                            max(dp(i+1,j),dp(i+1,j+1)));
}

int main(){
    ios::sync_with_stdio(false);
    memset(d,-1,sizeof(d)); //清空数组-1;
    cin>>n;
    input();
    cout<<dp(1,1);
    return 0;
}

record

time: O ( n 2 ) O(n^2) O(n2)


我们的问题结束了.
至于递推dp,请你自己研究,思想同理.代码如下:

code


做完这道题,你还可以试试:

T1
T2

感谢大家兹瓷



评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值