【PHP解法==LeetCode(动态规划2-最优子结构)】343.整数拆分 && 91.解码方法 && 62.63不同路径

目录

343.整数拆分

91.解码方法

62.不同路径

63.不同路径II


最优子结构:通过求子问题的最优解,可以获得原问题的最优解 


343.整数拆分

给定一个正整数 n,将其拆分为至少两个正整数的和,并使这些整数的乘积最大化。 返回你可以获得的最大乘积。

示例 1:

输入: 2
输出: 1
解释: 2 = 1 + 1, 1 × 1 = 1。

示例 2:

输入: 10
输出: 36
解释: 10 = 3 + 3 + 4, 3 × 3 × 4 = 36。

说明: 你可以假设 不小于 2 且不大于 58。

分析:如下图,以4为分割数为例

(1)想获得分割4的乘积可以有3种方案,可以分为 1+3,2+2,3+1,所以该问题可以化成求分割3/2/1所获得的最大乘积

 

(2)同理,分割3的方案可以分为,分割2和分割1,分割2又可以分割 1的方案,1无法再继续进行分割

(3)以此类推,可以求得所有情况可以获得的最大乘积,但如下图可以知道,有很多重复计算的分支,如分割2获得最大乘积就重复计算了两次,因此这里就可以采用记忆化搜索的方式去解答

 

 

直接相出动态规划的解法可能相对有些困难,但是如果通过自顶向下的记忆化搜索,就可以比较方便的推算出自底向上的动态规划算法

/*********  普通的递归:超出时间限制  *********/
class Solution {
    /**
     * @param Integer $n
     * @return Integer
     */
    function integerBreak($n) {
        return $this->breakInterger($n);
    }
    /**
     * [将n进行分割(至少分割两部分),可以获得的最大乘积]
     * @param $n [当前的数字]
     */
    private function breakInterger($n){
        if($n == 1) return 1;
        $res = -1;
        for($i = 1;$i<=$n-1;++$i){
            //这样可以获得,i + ( n - i)
            $res = max($res, $i * ($n-$i), $i * $this->breakInterger($n - $i));
        }
        return $res;
    }
}
/*********  记忆化搜索:16ms  *********/
class Solution {
    private $memory = [];      //初始化记忆数组
    /**
     * @param Integer $n
     * @return Integer
     */
    function integerBreak($n) {
        return $this->breakInterger($n);
    }
    /**
     * [将n进行分割(至少分割两部分),可以获得的最大乘积]
     * @param $n [当前的数字]
     */
    private function breakInterger($n){
        if($n == 1) return 1;       //当数=1时,已经无法继续分割,直接返回
        //当之前已经找到过该数的分割最大乘积时,可以直接返回
        if(isset($this->memory[$n])) return $this->memory[$n];  
        //否则则继续下面的流程
        $res = -1;
        for($i = 1;$i<=$n-1;++$i){
            //这样可以获得,i + ( n - i)
            $res = max($res, $i * ($n-$i), $i * $this->breakInterger($n - $i));
        }
        $this->memory[$n] = $res;   //将该数取得的最大结果写入记忆数组中
        return $res;
    }
}
/*********  动态规划:16ms  *********/
class Solution {  
    /**
     * @param Integer $n
     * @return Integer
     */
    function integerBreak($n) {
        $memory = [];   //初始化记忆数组
        $memory[1] = 1; //$memory[$i]表示将$i进行分割(至少分割两部分)后获得的最大乘积
        for($i = 2;$i<=$n;++$i){
            //求解$memory[$i]
            for($j = 1;$j<=$i-1;++$j){
                //$j + ($i - $j)
                //当前数的乘积取
                $memory[$i] = max(empty($memory[$i])?0:$memory[$i], $j * ($i - $j),$j * $memory[$i-$j]);
            }
        }
        return $memory[$n];
    }
}

91.解码方法

一条包含字母 A-Z 的消息通过以下方式进行了编码:

'A' -> 1
'B' -> 2
...
'Z' -> 26

给定一个只包含数字的非空字符串,请计算解码方法的总数。

示例 1:

输入: "12"
输出: 2
解释: 它可以解码为 "AB"(1 2)或者 "L"(12)。

示例 2:

输入: "226"
输出: 3
解释: 它可以解码为 "BZ" (2 26), "VF" (22 6), 或者 "BBF" (2 2 6) 。

动态规划:化为每一个子问题的最优解==>化为每一个位置的最多种解码可能,直到最后一个位置

注意:

(1)前两个数字,比较关键,需要进行特殊处理

(2)如果当前数字为0,则不进行单独考虑,当前dp数组为0

(3)如果当前数字不为0,则可配合前1个dp进行编码,当前$dp[$i] = $dp[$i-1]

(4)如果当前数字和前一个数字搭配小于26,则这两个数字可配合前2个dp进行编码,当前$dp[$i] += $dp[$i-2]

class Solution {
    /**
     * @param String $s
     * @return Integer
     */
    function numDecodings($s) {
        $dp = [];
        $len = strlen($s);          //初始化动态规划数组和长度
        if($len == 0) return 0;     //若长度为0,不能编码,直接返回0
        //初始化前两个数组的编码种类
        $dp[0] = $s[0] == '0' ? 0:1;
        if($len == 1) return $dp[0];//当长度为 1 时,返回第一个字母的动态规划解
        $flag = ($s[0] > 0 && $s[1] > 0) ? 1:0; //判断前两位有 0 的特殊情况
        $dp[1] = $flag + (($s[0] == 1 || ($s[0] == 2 && $s[1] <= 6)) ? 1:0);              //小于26时,再+1种可能
        for($i = 2;$i<$len;++$i){                   //从第3位继续计算
            //如果当前的数字为 0 ,需要与前一位一起考虑,不单独考虑
            $dp[$i] = $s[$i] == 0 ? 0:$dp[$i-1];    
            //当前位置跟前一位是否<=26,如果符合,则在 $i - 2 位上解码 
            $dp[$i] += ($s[$i-1] == 1 || ($s[$i-1] == 2 && $s[$i] <= 6)) ? $dp[$i-2] : 0;
        }
        return $dp[$len-1];
    }
}

62.不同路径

一个机器人位于一个 m x n 网格的左上角 (起始点在下图中标记为“Start” )。

机器人每次只能向下或者向右移动一步。机器人试图达到网格的右下角(在下图中标记为“Finish”)。

问总共有多少条不同的路径?

例如,上图是一个7 x 3 的网格。有多少可能的路径?

说明:m 和 的值均不超过 100。

示例 1:

输入: m = 3, n = 2
输出: 3
解释:
从左上角开始,总共有 3 条路径可以到达右下角。
1. 向右 -> 向右 -> 向下
2. 向右 -> 向下 -> 向右
3. 向下 -> 向右 -> 向右

示例 2:

输入: m = 7, n = 3
输出: 28

动态规划:化为每一个子问题的最优解==>每一个点的坐标值化为左上角到该点的路径总数

class Solution {
    /**
     * @param Integer $m
     * @param Integer $n
     * @return Integer
     */
    function uniquePaths($m, $n) {
        $dp = [];
        for($i = 0;$i<$m;++$i){
            for($j = 0;$j<$n;++$j){
                if($i == 0 || $j == 0) {    //边界都只有一条路径
                    $dp[$i][$j] = 1;
                }else{
                    $dp[$i][$j] = $dp[$i-1][$j] + $dp[$i][$j-1];    //其他方向的路径为该点的左边和上边的路径总和
                }
            }
        }
        return $dp[$m-1][$n-1]; //右下角的点的值为最终的结果
    }
}

63.不同路径II

现在考虑网格中有障碍物。那么从左上角到右下角将会有多少条不同的路径?

网格中的障碍物和空位置分别用 1 和 0 来表示。

说明:m 和 的值均不超过 100。

示例 1:

输入:
[
  [0,0,0],
  [0,1,0],
  [0,0,0]
]
输出: 2
解释:
3x3 网格的正中间有一个障碍物。
从左上角到右下角一共有2条不同的路径:
1. 向右 -> 向右 -> 向下 -> 向下
2. 向下 -> 向下 -> 向右 -> 向右

与62区别:输入参数改为数组;路径途中存在障碍物

解法区别:如果该点为阻碍点,则该点的dp为0

class Solution {
    /**
     * @param Integer[][] $obstacleGrid
     * @return Integer
     */
    function uniquePathsWithObstacles($obstacleGrid) {
        if(empty($obstacleGrid)) return 0;      //初始化判断,数组为空,则没有路径
        $dp = [];
        $m = count($obstacleGrid);      //初始化列
        $n = count($obstacleGrid[0]);   //初始化行
        for($i = 0;$i<$m;++$i){
            for($j = 0;$j<$n;++$j){
                if($obstacleGrid[$i][$j] == 0){     //当没有阻碍时
                    if($i == 0 && $j == 0){         //初始化左上角节点
                        $dp[$i][$j] = 1;
                    }else{
                        $dp[$i][$j] = $dp[$i-1][$j] + $dp[$i][$j-1];//其他方向的路径为该点的左边和上边的路径总和
                    }
                }else{
                    $dp[$i][$j] = 0;    //当没有阻碍时,该点不可通过,dp为0
                }
            }
        }
        return $dp[$m-1][$n-1];
    }
}

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值