SSL1100 USACO 1.5 数字金字塔(DP)


原题链接

外网进不去

题目大意

给你一个 n n n行的数字三角形,数字三角形的格式如下:

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

对于每一个数,你可以走到在他下面的两个数。
求出从顶部开始走到底部经过数字的最大总和。
S a m p l e \mathbf{Sample} Sample I n p u t \mathbf{Input} Input

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

S a m p l e \mathbf{Sample} Sample O u t p u t \mathbf{Output} Output

30

H i n t & E x p l a i n \mathbf{Hint\&Explain} Hint&Explain
路径为7-3-8-7-5,总和为30

解题思路

本题可以用两种方法,顺推逆推
①顺推
顾名思义,顺推就是从三角形的最顶上开始推,一直推到最后一行。
d p dp dp转移方程如下:
f i , j f_{i,j} fi,j为从第 1 1 1行第 1 1 1列推到第 i i i行第 j j j列的最大值, a i , j a_{i,j} ai,j为三角形第 i i i行第 j j j列的数字
f i , j = { a i , j i = 1 f i − 1 , j + a i , j j = 1 m a x ( f i − 1 , j   ,   f i − 1 , j − 1 ) + a i , j 1 < i ≤ n , 1 < j ≤ n f_{i,j}=\begin{cases} a_{i,j}&i=1 \\f_{i-1,j}+a_{i,j}&j=1 \\max(f_{i-1,j}\ ,\ f_{i-1,j-1})+a_{i,j}& 1<i\le n,1<j\le n \end{cases} fi,j=ai,jfi1,j+ai,jmax(fi1,j , fi1,j1)+ai,ji=1j=11<in,1<jn
而答案就是 max ⁡ 1 ≤ i ≤ n { f n , i } \max_{1\le i\le n}\{f_{n,i}\} 1inmax{fn,i}
解题思路写了,代码随即而来:

#include<iostream>
using namespace std;

int n;
int a[1001][1001];
int f[1001][1001];

int main()
{
    cin>>n;
    for(int i=1; i<=n; i++)
        for(int j=1; j<=i; j++)
        {
            cin>>a[i][j];
        }
    f[1][1]=a[1][1];
    int res=0;
    for(int i=2; i<=n; i++)
    {
        for(int j=1; j<=i; j++)
        {
            f[i][j]=max(f[i-1][j],f[i-1][j-1])+a[i][j];
            if(i==n) res=max(res,f[i][j]);
        }
    }
    cout<<res;
    return 0;
}

②逆推
就是顺推反过来,从第 n n n行推到第一行。
d p dp dp转移方程如下:
f i , j f_{i,j} fi,j为从第 n n n(没有列) 推到第 i i i行第 j j j列的最大值, a i , j a_{i,j} ai,j为三角形第 i i i行第 j j j列的数字
f i , j = { a i , j i = n f i − 1 , j + a i , j j = 1 f i − 1 , j + f i − 1 , j − 1 + a i , j 1 ≤ i < n , 1 < j ≤ n f_{i,j}=\begin{cases} a_{i,j}&i=n \\f_{i-1,j}+a_{i,j}&j=1 \\f_{i-1,j}+f_{i-1,j-1}+a_{i,j}&1\le i<n,1<j\le n \end{cases} fi,j=ai,jfi1,j+ai,jfi1,j+fi1,j1+ai,ji=nj=11i<n,1<jn
而答案就是 f 1 , 1 f_{1,1} f1,1
代码在下面 ∼ \sim


相比之下,逆推比较好,因为他的答案就是 f 1 , 1 f_{1,1} f1,1,而顺推则还要加一个 f o r for for来判断答案,虽然也不是太难,但能方便一点就方便一点吧。

上代码

此为逆推代码

#include<iostream>
using namespace std;

int n;
int a[1001][1001];
int f[1001][1001];

int main()
{
    freopen("xxx.in","r",stdin);
    freopen("xxx.out","w",stdout);
    cin>>n;
    for(int i=1; i<=n; i++)
        for(int j=1; j<=i; j++)
        {
            cin>>a[i][j];
            if(i==n) f[i][j]=a[i][j];
        }
    for(int i=n-1; i>=1; i--)
    {
        for(int j=1; j<=i; j++)
        {
            f[i][j]=max(f[i+1][j],f[i+1][j+1])+a[i][j];
        }
    }
    cout<<f[1][1];
    fclose(stdin);
    fclose(stdout);
    return 0;
}

完美切题 ∼ \sim

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值