zoukankan      html  css  js  c++  java
  • 【算法学习笔记】31.动态规划 SJTU OJ 1320 numtri

    Description

    Consider the number triangle shown below. Write a program that calculates the highest sum of numbers that can be passed on a route that starts at the top and ends somewhere on the base. Each step can go either diagonally down to the left or diagonally down to the right.

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

    In the sample above, the route from 7 to 3 to 8 to 7 to 5 produces the highest sum: 30.

    Input Format

    The first line contains R (1 <= R <= 1000), the number of rows. Each subsequent line contains the integers for that particular row of the triangle. All the supplied integers are non-negative and no larger than 100.

    Output Format

    A single line containing the largest sum using the traversal specified.

    Sample Input:

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

    Sample Output:

    30


    利用动态规划的思想,可以从第二排开始更新节点,使节点的值变为从它之前选取的最大值.语言叙述比较麻烦,以样例来说就是

    7
    10 15
    18 16 15
    20 25 20 19
    24 30 27 26 24

    所以选最后一排里最大的那个数即可,但是这种更新方法比较麻烦的是要考虑边界情况,比如 每行的两个端点的处理比较麻烦
    根本原因是因为" 找到一个节点的前驱节点的过程与节点位置有关 "

    因此想到 找一个节点的后继节点 是不是更方便一点? 是的.因为所有的节点都有两个子节点.
    所以更新过程变为了从倒数第二排开始向第一排更新
    最后一排不动:4 5 2 6 5
    倒数第二排开始更新:
    7 12 10 10
    20 13 10
    23 21
    30
    所以更新之后的第一个节点即是答案,也避免了寻找最大值的过程, 非常简便.

    这个算法属于动态规划的思想, 因为它存储了可能重复计算的结果(或者说,它在本质上不存在重复计算),每一个阶段就是每一行的状态集合,
    每一个点的状态S[x]就是指,在原树的基础上从这个点开始出发到最后的路径里最大的和.
    状态转移方程就是 S[x] = R[x] + max(S[x.leftson],S[x.rightson])
    计算状态时 要用到x的下一层 也暗示了我们要从底向上遍历.
    代码如下:
    #include <iostream>
    using namespace std;
    
    int R[1000000] = {0};
    int N;
    inline int getLayFirstId(int layId){
        return (1+layId-1)*(layId-1)/2 + 1;
    } 
    inline int getLeftSon(int node , int layId){
        return getLayFirstId(layId+1) + node - getLayFirstId(layId);
    }
    //从 curId开始向下选择 curLay是当前行 curSum 是之前的和
    int Calc(int curId, int curLay, int curSum){
        int left = getLeftSon(curId,curLay);
        int right = left+1;
        if(curLay == N-1)
            return R[left] > R[right] ? curSum + R[left] : curSum + R[right];
        int lsum = Calc(left,curLay+1,curSum) + R[curId];
        int rsum = Calc(right,curLay+1,curSum) + R[curId];
        return lsum > rsum ? lsum : rsum; 
    }
    
    inline int getMAX(int a, int b){
        return a>b?a:b;
    }
    
    //从N-1排开始更新
    inline void updateLayer(int layId){
        for (int i = 0; i < layId; ++i)
        {
            int node = getLayFirstId(layId)+i;
            int left = getLeftSon(node,layId);
            int right = left+1;
            R[node] += getMAX(R[left],R[right]);
        }
    }
    
    int main(int argc, char const *argv[])
    {
         cin>>N;
        for (int i = 1; i <= N*(N+1)/2; ++i)
            cin>>R[i];    //从R[1]开始存储 为了计算方便
        
        for (int k = N-1; k >= 1; k--) 
            updateLayer(k);
        
        cout<<R[1]<<endl;
        return 0;
    }
    View Code

    PS1:此题用贪心法可以得60分, 贪心策略就是在决定选左还是选右时,先进行试探,然后根据左的左 左的右 右的左 右的右 这四个节点来进行决策.
    PS2:此题也可以用分治法来进行暴力搜索DFS 在DFS的过程中此题不能用回溯法,因为所有解都是可行解,我们找的是最优解,最优解一般会想到用分支界限法来剪枝,但是此题节点值并没有顺序(不像埃及分数那样),所以也不可以.即使可以也会有重复计算.
  • 相关阅读:
    使用Dictionary键值对判断字符串中字符出现次数
    Linq实现字符串拼接多条件查询
    js数据类型转换
    js前端数据类型检测typeof,instanceof,Object.prototype.toString.call
    moment.js格式化日期,获取前一个月的时间
    css 样式中height100%失效问题
    记一次react项目运行崩溃
    null和undefined区别
    windows腾讯云/阿里云服务器更换操作系统为linux
    csrf攻击原理和防御-生成token防御代码
  • 原文地址:https://www.cnblogs.com/yuchenlin/p/sjtu_oj_1320.html
Copyright © 2011-2022 走看看