【PHP解法==LeetCode(动态规划1)】70.爬楼梯 && 120.三角形最小路径和 && 64.最小路径和

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/u010365335/article/details/88046329

目录

70.爬楼梯

120.三角形最小路径和

64.最小路径和


大多数的动态规划,本质都是递归问题,只是在递归的过程中会有很多的重叠子问题

对于重叠子问题,可以用记忆化搜索:自顶向下的解决问题,而动态规划是自底向上的解决问题。

动态规划:将原问题拆解成若干子问题,同时保存子问题的答案,使得每个子问题只求解一次,最终获得原问题的答案

因此我们在做动态规划问题时,可以先思考他的记忆化搜索模型,在去推算出他的动态规划模型,比较好想到


70.爬楼梯

假设你正在爬楼梯。需要 n 阶你才能到达楼顶。

每次你可以爬 1 或 2 个台阶。你有多少种不同的方法可以爬到楼顶呢?

注意:给定 n 是一个正整数。

示例 1:

输入: 2
输出: 2
解释: 有两种方法可以爬到楼顶。
1.  1 阶 + 1 阶
2.  2 阶

示例 2:

输入: 3
输出: 3
解释: 有三种方法可以爬到楼顶。
1.  1 阶 + 1 阶 + 1 阶
2.  1 阶 + 2 阶
3.  2 阶 + 1 阶

普通的递归查询,需要查询所有的情况,在LeetCode上是无法通过的

但是会有很多次重复的查询,如下图n-2

因此可以采用记忆化搜索对这些重复计算的分支减去,再者,可以通过记忆化搜索的思想,转变成递推

也就是动态规划的解决方法

/*********  普通的递归过程:LeetCode无法通过  *********/
class Solution {
    /**
     * @param Integer $n
     * @return Integer
     */
    function climbStairs($n) {
        return $this->calway($n);
    }
    private function calway($n){
        if($n == 0 || $n ==1) return 1;     //层层递归过程
        return $this->calway($n-1) + $this->calway($n-2);
    }
}
/*********  记忆化搜索:LeetCode 16ms  *********/
class Solution {
    private $memory = [];
    /**
     * @param Integer $n
     * @return Integer
     */
    function climbStairs($n) {
        return $this->calway($n);
    }
    private function calway($n){
        if($n == 0 || $n ==1) return 1;
        if(empty($this->memory[$n]))
            $this->memory[$n] = $this->calway($n-1) + $this->calway($n-2);
        return $this->memory[$n];
    }
}
/*********  动态规划:LeetCode 12ms  *********/
class Solution {
    /**
     * @param Integer $n
     * @return Integer
     */
    function climbStairs($n) {
        $memory = [];               //自下而上
        $memory[0] = 1;             //递推,爬上0阶楼梯有1种方法,往上推
        $memory[1] = 1;
        for($i = 2;$i<=$n;++$i){
            $memory[$i] = $memory[$i-1] + $memory[$i-2];
        }
        return $memory[$n];
    }
}

120.三角形最小路径和

给定一个三角形,找出自顶向下的最小路径和。每一步只能移动到下一行中相邻的结点上。

例如,给定三角形:

[
     [2],
    [3,4],
   [6,5,7],
  [4,1,8,3]
]
自顶向下的最小路径和为 11
(即,2 + 3 + 5 + 1 = 11)。

说明:

如果你可以只使用 O(n) 的额外空间(n 为三角形的总行数)来解决这个问题,那么你的算法会很加分。

普通的递归:内部的节点都要重复计算两次因此肯定会超出时间限制

所以可以采用记忆化搜索和动态规划的解法

记忆化搜索:自顶向下,会记住节点对应的最小路径和

动态规划:自底向上,计算每一层的节点往下的最小路径和,最后结果就是顶点

/**********  普通递归:LeetCode 超出时间限制  **********/
class Solution {
    private $triangle,$len;
    /**
     * @param Integer[][] $triangle
     * @return Integer
     */
    function minimumTotal($triangle) {
        $this->len = count($triangle) - 1;
        $this->triangle = $triangle;
        $res = $this->minPath(0,0);         //从第0个开始往下层递归
        return $res;
    }
    function minPath($row,$col){
        $res = $this->triangle[$row][$col];
        if($row == $this->len)  return $res;
        $add = min($this->minPath($row+1,$col),$this->minPath($row+1,$col+1));  //去下方两条路中和比较小的一条
        return $res + $add;     //与上一层元素相加返回
    }
}
/**********  记忆化搜索递归:56 ms  **********/
class Solution {
    private $triangle,$len;             //初始化三角形表格和数组高度
    private $memory;                    //初始化记忆数组
    /**
     * @param Integer[][] $triangle
     * @return Integer
     */
    function minimumTotal($triangle) {
        $this->len = count($triangle) - 1;
        $this->triangle = $triangle;
        $res = $this->minPath(0,0);
        return $res;
    }
    function minPath($row,$col){
        $res = $this->triangle[$row][$col];
        if($row == $this->len)  return $res;
        if(empty($this->memory[$row+1][$col]))                          //记录该元素点往下的最小和
            $this->memory[$row+1][$col] = $this->minPath($row+1,$col);
        if(empty($this->memory[$row+1][$col+1]))
            $this->memory[$row+1][$col+1] = $this->minPath($row+1,$col+1);
        $add = min($this->memory[$row+1][$col],$this->memory[$row+1][$col+1]);  //两条路径的最小和
        return $res + $add;
    }
}
/**********  动态规划:32 ms  **********/
class Solution {
    /**
     * @param Integer[][] $triangle
     * @return Integer
     */
    function minimumTotal($triangle) {
        $len = count($triangle);
        if($len == 0) return 0;
        if($len == 1) return $triangle[0][0];   //从下往上,依次取该节点的最小路径和
        for($i = $len-2;$i>=0;--$i){            //第一层保持原样不需要计算
            $colen = count($triangle[$i]);
            for($j = 0;$j < $colen;++$j){
                $triangle[$i][$j] += min($triangle[$i+1][$j],$triangle[$i+1][$j+1]);    //加上该节点对应的下方两条路径的最大和
            }
        }
        return $triangle[0][0];                 //最后调用最顶部的元素就是最终答案
    }
}

64.最小路径和

给定一个包含非负整数的 m x n 网格,请找出一条从左上角到右下角的路径,使得路径上的数字总和为最小。

说明:每次只能向下或者向右移动一步。

示例:

输入:
[
  [1,3,1],
  [1,5,1],
  [4,2,1]
]
输出: 7
解释: 因为路径 1→3→1→1→1 的总和最小。

该题也可以用递归,记忆化搜索和动态规划来解决,该题是比较简单的动态规划问题

解法:

(1)遍历整张图,从左上到右下,让每一个节点的值都是该点到左上角的最小路径

(2)其中需要注意,左边界和上边界都只有一条路径,不需要进行最小化的判断,其他节点需要加上取左或上元素中偏小的节点

(3)最终的结果就是右下角的节点,返回即可

class Solution {
    /**
     * @param Integer[][] $grid
     * @return Integer
     */
    function minPathSum($grid) {
        $row = count($grid);
        if($row == 0) return 0;
        $col = count($grid[0]);
        for($i = 0;$i<$row;++$i){
            for($j = 0;$j<$col;++$j){       //遍历整个图,构造动态规划
                if($i == 0){                //每个节点都是从左上角到该点的最短路径
                    $grid[$i][$j] += empty($grid[$i][$j-1]) ? 0:$grid[$i][$j-1];    //上边界
                }elseif($j == 0){
                    $grid[$i][$j] += $grid[$i-1][$j];   //下边界都只有一条路径,因此不用进行帕努单
                }else{ 
                    $grid[$i][$j] += min($grid[$i-1][$j],$grid[$i][$j-1]);  //下方路径取左或上较小的节点
                }
            }
        }
        return $grid[$row-1][$col-1];
    }
}

猜你喜欢

转载自blog.csdn.net/u010365335/article/details/88046329