zoukankan      html  css  js  c++  java
  • 【动态规划基础】数字金字塔

    题目链接:1258:【例9.2】数字金字塔

    1258:【例9.2】数字金字塔


    时间限制: 1000 ms         内存限制: 65536 KB
    提交数: 9635     通过数: 5467

    【题目描述】

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

    在上面的样例中,从13到8到26到15到24的路径产生了最大的和86。

    【输入】

    第一个行包含R(1≤ R≤1000),表示行的数目。

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

    所有的被供应的整数是非负的且不大于100。

     

    【输出】

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

    【输入样例】

    5
    13
    11 8
    12 7  26
    6  14 15 8
    12 7  13 24 11

    【输出样例】

    86

     

     

    提交 统计信息 提交记录

     

     

     

      

     【动态规划基础】数字金字塔

      经典的动态规划

    方法一:贪心

      题目里说了,从上往下走,要求出路径的最大值,那么我最先想到的方法就是——

      你每次都走最大的一边就可以了啊。

      就像这样:

      

      但是,如果你是一个善于造数据来验证你的算法的人,你很快就会发现自己算法的问题,就像这样:

    1
    2 1
    1 2 9
    1 2 8 9
    3 3 2 9 9

      在这一组数据中,如果使用贪心算法,就会一开始就左边,从而错失下面全是9的右边。

      也就是说,贪心算法看得太近了,没有顾全大局。

    方法二:搜索 & 记忆化搜索

      像我这样的暴力型选手,碰见什么题都要用搜索写一遍。可以对拍不说,写不出来的时候,还可以骗分。

    #include <bits/stdc++.h>
    using namespace std;
    
    int jzt[1005][1005], maxn, summ, n;
    
    void dfs(int i, int j){
        if(i==n){
            maxn=maxn>summ?maxn:summ;
        }
        else{
            summ+=jzt[i+1][j];
            dfs(i+1, j);
            summ-=jzt[i+1][j];
            summ+=jzt[i+1][j+1];
            dfs(i+1, j+1);
            summ-=jzt[i+1][j+1];
        }
    }
    
    int main(){
        scanf("%d", &n);
        for(int i=0; i<n; i++){
            for(int j=0; j<=i; j++){
                scanf("%d", &jzt[i][j]);
            }
        }
    
        dfs(0, 0);
        printf("%d", maxn+jzt[0][0]);
    
        return 0;
    }

       T了。。

      但是没关系,我们还有搜索优化利器:记忆化。

      

       通过观察与推理,我们会发现,中间的这些点到底端的最优解会被重复计算,所以我们要把这些状态记忆下来,四舍五入约等于全部记下来(其实是我懒得做特判)。。

    #include <bits/stdc++.h>
    using namespace std;
    
    int n, bz[1005][1005], jzt[1005][1005];
    
    int dfs(int x, int y){
        if(x==n-1) return jzt[x][y];
        else{
            if(bz[x][y]) return jzt[x][y];
            else{
                bz[x][y]=1;
                jzt[x][y]+=max(dfs(x+1, y), dfs(x+1, y+1));
                return jzt[x][y];
            }
        }
    }
    
    int main(){
        scanf("%d", &n);
        for(int i=0; i<n; i++){
            for(int j=0; j<=i; j++){
                scanf("%d", &jzt[i][j]);
            }
        }
        printf("%d", dfs(0, 0));
    
        return 0;
    }

      这次,就功地AC了,不得不感叹记忆化的强大。其实记忆化搜索的用时就已经相当于动态规划了(这也是为什么我把搜索学好后才学动态规划)。。

    方法三:动态规划(递推)

      现在,想象一下,通过某些神奇的算法,你已经成功走到了倒数第二行的某一个位置,那么下一步应该怎么走呢?

      很明显,只要走最大的那个就可以了。因为这里只有最后一步了,局部最优解就等于全局最优解。

      但事实是,你并不知道这个神奇的算法。。。所以你还是不知道你会走到倒数第二行的哪个位置。

      但是,无论如何,你最终都会走到倒数第二行的n-1个点之一。那么我们就把到任意一个点的全局最优解求出来吧。

      那么知道了倒数第二行怎么走,那么我们就再进一步吧。

    通过某些神奇的算法,你已经成功走到了 n-2 行(倒数第三行)的某一个位置,那么下一步应该怎么走呢?

       这时,我们就不能通过单纯的比大小来决定了。

      但是,我们通过上一步的计算,已经知道了走到 n-1 行的最优解,而且,我们也可以算出来走到 n-1 行某一个位置之后走到的点的和(这里指之前算出的最优解)

      那么问题就很简单了,只需要走这个值最大的一边就可以了。

      

      重复这个过程。。。

      最后:

    通过某些神奇的算法,你已经成功走到了第 1 行的某一个位置,那么下一步应该怎么走呢?

      显然,我们已经知道了下一步该怎么走,,而且这时,我们就可以考虑这个“神奇的算法”了。

      由于第一行只有一个数,所以我们可以肯定的是,我们走到的一定是这个位置。

      最后,再算出第一步该怎么走时,你就算出了这个最优路径。

    代码:

    #include <bits/stdc++.h>
    using namespace std;
    
    int n, a[1005][1005];
    
    int main(){
        cin >> n;
        for(int i=0; i<n; i++){
            for(int j=0; j<=i; j++){
                cin >> a[i][j];
            }
        }
        for(int i=n-2; i>=0; i--){
            for(int j=0; j<=i; j++){
                a[i][j] += max(a[i+1][j], a[i+1][j+1]);
            }
        }
        cout << a[0][0];
    
        return 0;
    }
  • 相关阅读:
    题解-bzoj1283序列 & bzoj4842 [Neerc2016]Delight for a Cat
    题解-bzoj4061 CERC-2012Farm and Factory
    题解-bzoj3569 DZY Loves Chinese II
    题解-bzoj3901 棋盘游戏
    题解-PKUWC2018 Minimax
    题解-PKUWC2018 Slay the Spire
    题解-PKUWC2018 随机算法
    题解-PKUWC2018 随机游走
    bzoj1010[HNOI2008]玩具装箱toy 斜率优化dp
    bzoj1096[ZJOI2007]仓库建设 斜率优化dp
  • 原文地址:https://www.cnblogs.com/dong628/p/11616193.html
Copyright © 2011-2022 走看看