面试经典150题——多维动态规划

文章目录

  • 1、三角形最小路径和
    • 1.1 题目链接
    • 1.2 题目描述
    • 1.3 解题代码
    • 1.4 解题思路
  • 2、最小路径和
    • 2.1 题目链接
    • 2.2 题目描述
    • 2.3 解题代码
    • 2.4 解题思路
  • 3、不同路径 II
    • 3.1 题目链接
    • 3.2 题目描述
    • 3.3 解题代码
    • 3.4 解题思路
  • 4、最长回文子串
    • 4.1 题目链接
    • 4.2 题目描述
    • 4.3 解题代码
    • 4.4 解题思路
  • 5、交错字符串
    • 5.1 题目链接
    • 5.2 题目描述
    • 5.3 解题代码
    • 5.4 解题思路
  • 6、编辑距离
    • 6.1 题目链接
    • 6.2 题目描述
    • 6.3 解题代码
    • 6.4 解题思路
  • 7、买卖股票的最佳时机 III
    • 7.1 题目链接
    • 7.2 题目描述
    • 7.3 解题代码
    • 7.4 解题思路
  • 8、买卖股票的最佳时机 IV
    • 8.1 题目链接
    • 8.2 题目描述
    • 8.3 解题代码
    • 8.4 解题思路
  • 9、最大正方形
    • 9.1 题目链接
    • 9.2 题目描述
    • 9.3 解题代码
    • 9.4 解题思路


1、三角形最小路径和

1.1 题目链接

点击跳转到题目位置

1.2 题目描述

给定一个三角形 triangle ,找出自顶向下的最小路径和。

每一步只能移动到下一行中相邻的结点上。相邻的结点 在这里指的是 下标上一层结点下标 相同或者等于 上一层结点下标 + 1 的两个结点。也就是说,如果正位于当前行的下标 i ,那么下一步可以移动到下一行的下标 i 或 i + 1 。

提示:

  • 1 <= triangle.length <= 200
  • triangle[0].length == 1
  • triangle[i].length == triangle[i - 1].length + 1
  • -104 <= triangle[i][j] <= 104

1.3 解题代码

class Solution {
    public int minimumTotal(List<List<Integer>> triangle) {
        int n = triangle.size();
        int[] dp = new int[n];
        for(int i = n - 1; i >= 0; --i){
            for(int j = 0; j < triangle.get(i).size(); ++j){
                if(i == n - 1){
                    dp[j] = triangle.get(i).get(j);
                } else{
                    dp[j] = triangle.get(i).get(j) + Math.min(dp[j], dp[j + 1]);
                }
            }
        }
        return dp[0];
    }
}

1.4 解题思路

  1. 动态规划。
  2. 题目中描述每一步只能移动到下一行中相邻的结点上,则状态转移则是由下面的节点向上面的节点转移,立从最下面到当前的节点的最小值为当前值加上到达下面两个相邻节点中的一个的最小路径和。

2、最小路径和

2.1 题目链接

点击跳转到题目位置

2.2 题目描述

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

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

提示:

  • m == grid.length
  • n == grid[i].length
  • 1 <= m, n <= 200
  • 0 <= grid[i][j] <= 200

2.3 解题代码

class Solution {
    public int minPathSum(int[][] grid) {
        int m = grid.length;
        int n = grid[0].length;
        for(int i = 0; i < m; ++i){
            for(int j = 0; j < n; ++j){
                if(i == 0 && j == 0){
                    continue;
                } else if(i == 0){
                    grid[i][j] += grid[i][j - 1];
                } else if(j == 0){
                    grid[i][j] += grid[i - 1][j];
                } else{
                    grid[i][j] += Math.min(grid[i - 1][j], grid[i][j - 1]);
                }
            }
        }
        return grid[m - 1][n - 1];
    }
}

2.4 解题思路

  1. 动态规划,dp[i][j]表示到达位置(i,j)的最小路径和。
  2. 如果 i == 0 并且 j == 0 的时候,值为grid[0][0]。
  3. 如果 i == 0 并且 j != 0 的时候,值是从 (i , j - 1)状态转移过来的。
  4. 如果i != 0 并且 j == 0 的时候,值是从(i - 1, j)状态转移过来的。
  5. 如果i != 0 并且 j != 0 的时候,值是从 (i - 1, j) 和 (i, j - 1)状态的小者转移过来的。

3、不同路径 II

3.1 题目链接

点击跳转到题目位置

3.2 题目描述

给定一个 m x n 的整数数组 grid。一个机器人初始位于 左上角(即 grid[0][0])。机器人尝试移动到 右下角(即 grid[m - 1][n - 1])。机器人每次只能向下或者向右移动一步。

网格中的障碍物和空位置分别用 1 和 0 来表示。机器人的移动路径中不能包含 任何 有障碍物的方格。

返回机器人能够到达右下角的不同路径数量。

测试用例保证答案小于等于 2 * 109

3.3 解题代码

class Solution {
    public int uniquePathsWithObstacles(int[][] obstacleGrid) {
        int m = obstacleGrid.length;
        int n = obstacleGrid[0].length;
        int[][] dp = new int[m][n];
        for(int i = 0; i < m; ++i){
            for(int j = 0; j < n; ++j){
                if(obstacleGrid[i][j] == 0){
                    if(i == 0 && j == 0){
                        dp[i][j] = 1;
                    } else if(j == 0){
                        dp[i][j] = dp[i - 1][j];
                    } else if(i == 0){
                        dp[i][j] = dp[i][j - 1];
                    } else{
                        dp[i][j] = dp[i - 1][j] + dp[i][j - 1]; 
                    }
                }
            }
        }
        return dp[m - 1][n - 1];
    }
}

3.4 解题思路

  1. 状态转移的过程与第2题一致。

4、最长回文子串

4.1 题目链接

点击跳转到题目位置

4.2 题目描述

给你一个字符串 s,找到 s 中最长的 回文子串。

提示:

  • 1 <= s.length <= 1000
  • s 仅由数字和英文字母组成

4.3 解题代码

class Solution {
    public String longestPalindrome(String s) {
        int m = s.length();
        int max_len = 0;
        int end = 0;
        boolean[][] dp = new boolean[m][m];
        for(int i = 0; i < m; ++i){
            for(int j = m - 1; j >= i; --j){
                if(i == 0){
                    dp[j - i][j] = true;
                } else{
                    if(i == 1){
                        dp[j - i][j] = (s.charAt(j - i) == s.charAt(j));
                    } else{
                        dp[j - i][j] = (s.charAt(j - i) == s.charAt(j) && dp[j - i + 1][j - 1]);
                    }
                }
                if(dp[j - i][j] == true){
                    max_len = i;
                    end = j;
                }
            }
        }
        return s.substring(end - max_len, end + 1);
    }
}

4.4 解题思路

  1. 动态规划,dp[i][j]表示字符串s从下标i到下标j的字符子串为回文串。
  2. 首先长度为1的字符子串一定是回文串。
  3. 长度大于等于2的字符子串,如果两端的字符相等,则其是否是回文串取决于dp[i + 1][j - 1]是否为true。

5、交错字符串

5.1 题目链接

点击跳转到题目位置

5.2 题目描述

给定三个字符串 s1、s2、s3,请你帮忙验证 s3 是否是由 s1 和 s2 交错 组成的。

两个字符串 s 和 t 交错 的定义与过程如下,其中每个字符串都会被分割成若干 非空 子字符串:

  • s = s1 + s2 + … + sn
  • t = t1 + t2 + … + tm
  • |n - m| <= 1
  • 交错 是 s1 + t1 + s2 + t2 + s3 + t3 + … 或者 t1 + s1 + t2 + s2 + t3 + s3 + …

**注意:**a + b 意味着字符串 a 和 b 连接。

提示:

  • 0 <= s1.length, s2.length <= 100
  • 0 <= s3.length <= 200
  • s1、s2、和 s3 都由小写英文字母组成

5.3 解题代码

class Solution {
    public boolean isInterleave(String s1, String s2, String s3) {
        int m = s1.length();
        int n = s2.length();
        int t = s3.length();
        if(m + n != t){
            return false;
        }
        boolean[][] dp = new boolean[m + 1][n + 1];
        dp[0][0] = true;
        for(int i = 0; i <= m; ++i){
            for(int j = 0; j <= n; ++j){
                int idx = i + j - 1;
                if(i > 0){
                    dp[i][j] |= (dp[i - 1][j] && (s3.charAt(idx) == s1.charAt(i - 1)));
                }
                if(j > 0){
                    dp[i][j] |= (dp[i][j - 1] && (s3.charAt(idx) == s2.charAt(j - 1)));
                }
            }
        }
        return dp[m][n];
    }
}

5.4 解题思路

  1. 动态规划
  2. dp[i][j] 表示选用字符串s1中第i - 1位置或者字符串中第j - 1位置的字符能否实现连接。
  3. i == 0 并且

6、编辑距离

6.1 题目链接

点击跳转到题目位置

6.2 题目描述

给你两个单词 word1 和 word2, 请返回将 word1 转换成 word2 所使用的最少操作数 。

你可以对一个单词进行如下三种操作:

  • 插入一个字符
  • 删除一个字符
  • 替换一个字符

提示:

  • 0 <= word1.length, word2.length <= 500
  • word1 和 word2 由小写英文字母组成

6.3 解题代码

class Solution {
    public int minDistance(String word1, String word2) {
        int m = word1.length();
        int n = word2.length();
        int[][] dp = new int[m + 1][n + 1];
        for(int i = 0; i <= m; ++i){
            dp[i][0] = i;
        }
        for(int j = 0; j <= n; ++j){
            dp[0][j] = j;
        }

        for(int i = 1; i <= m; ++i){
            for(int j = 1; j <= n; ++j){
                int num1 = dp[i - 1][j] + 1;
                int num2 = dp[i][j - 1] + 1;
                int num3 = word1.charAt(i - 1) == word2.charAt(j - 1) ? dp[i - 1][j - 1] : dp[i - 1][j - 1] + 1;
                dp[i][j] = Math.min(Math.min(num1, num2), num3);
            }
        }
        return dp[m][n];
    }
}

6.4 解题思路

  1. 动态规划 dp[i][j] 表示word1 以从(0 ~ i - 1) 转化到 word2 从 (0 ~ j - 1)总共需要多少步。
  2. word1插入一个字符 等价于 word2 删除一个字符; word1删除一个字符 等价于 word2 插入一个字符; word1 替换一个字符 等价于 word2 替换一个字符。
  3. 所以对于dp[i][j]而言,可能是由三种状态转移过来的,一种是(i - 1, j),一种是(i, j - 1),一种是(i - 1, j - 1),前两者转化后再删除一个字符就可以到达j了,后面则需要判断word1和word2中新加入的字符是否相等,如果相等则不需要从上一个状态额外变化,否则仍然需要+1,最后最优值为三者的最小值。

7、买卖股票的最佳时机 III

7.1 题目链接

点击跳转到题目位置

7.2 题目描述

给定一个数组,它的第 i 个元素是一支给定的股票在第 i 天的价格。

设计一个算法来计算你所能获取的最大利润。你最多可以完成 两笔 交易。

**注意:**你不能同时参与多笔交易(你必须在再次购买前出售掉之前的股票)。

提示:

  • 1 <= prices.length <= 105
  • 0 <= prices[i] <= 105

7.3 解题代码

class Solution {
    public int maxProfit(int[] prices) {
        return maxProfit(2, prices);
    }

    public int maxProfit(int k, int[] prices) {
        int m = prices.length;
        k = Math.min(k, m / 2);
        // buy[i][j]表示在(0~i)中至少持有一只股票的情况下最大利润
        int[][] buy = new int[m][k + 1];
        // sell[i][j]表示在(0~i)中一只股票且卖出j只股票的最大利润
        int[][] sell = new int[m][k + 1];
        buy[0][0] = -prices[0];
        
        for(int i = 1; i <= k; ++i){
            buy[0][i] = Integer.MIN_VALUE / 2;
            sell[0][i] = Integer.MIN_VALUE / 2;
        }
        
        for (int i = 1; i < m; ++i) {
            buy[i][0] = Math.max(-prices[i], buy[i - 1][0]);
            for (int j = 1; j <= k; ++j) {
                buy[i][j] = Math.max(sell[i - 1][j] - prices[i], buy[i - 1][j]);
                sell[i][j] = Math.max(buy[i - 1][j - 1] + prices[i], sell[i - 1][j]);
            }
        }
        int max0 = 0;
        for(int i = 0; i <= k; ++i){
            max0 = Math.max(max0, sell[m - 1][i]);
        }
        return max0;
    }
}

7.4 解题思路

  1. 动态规划
  2. 题目与第8题思路一致,只需要在第8题的模板上将k改成数字2即可。

8、买卖股票的最佳时机 IV

8.1 题目链接

点击跳转到题目位置

8.2 题目描述

给你一个整数数组 prices 和一个整数 k ,其中 prices[i] 是某支给定的股票在第 i 天的价格。

设计一个算法来计算你所能获取的最大利润。你最多可以完成 k 笔交易。也就是说,你最多可以买 k 次,卖 k 次。

注意:你不能同时参与多笔交易(你必须在再次购买前出售掉之前的股票)。

提示:

  • 1 <= k <= 100
  • 1 <= prices.length <= 1000
  • 0 <= prices[i] <= 1000

8.3 解题代码

class Solution {
    public int maxProfit(int k, int[] prices) {
        int m = prices.length;
        k = Math.min(k, m / 2);
        // buy[i][j]表示在(0~i)中至少持有一只股票且卖出j次的情况下最大利润
        int[][] buy = new int[m][k + 1];
        // sell[i][j]表示在(0~i)中一只股票且卖出j只股票的最大利润
        int[][] sell = new int[m][k + 1];
        buy[0][0] = -prices[0];
        
        for(int i = 1; i <= k; ++i){
            buy[0][i] = Integer.MIN_VALUE / 2;
            sell[0][i] = Integer.MIN_VALUE / 2;
        }
        
        for (int i = 1; i < m; ++i) {
            buy[i][0] = Math.max(-prices[i], buy[i - 1][0]);
            for (int j = 1; j <= k; ++j) {
                buy[i][j] = Math.max(sell[i - 1][j] - prices[i], buy[i - 1][j]);
                sell[i][j] = Math.max(buy[i - 1][j - 1] + prices[i], sell[i - 1][j]);
            }
        }
        int max0 = 0;
        for(int i = 0; i <= k; ++i){
            max0 = Math.max(max0, sell[m - 1][i]);
        }
        return max0;
    }
}

8.4 解题思路

  1. 动态规划
  2. buy[i][j]表示在(0~i)中至少持有一只股票且卖出j次的情况下最大利润。
  3. sell[i][j]表示在(0~i)中一只股票且卖出j只股票的最大利润。
  4. 则首先初始化状态,对于二维数组,第一维度则为股票的数量,总共为m = prices.length - 1,第二维则为操作数量,要么操作k次,要么就为股票的数量 / 2(两者取最小值)(因为每次操作要么就是买股票,要么就是卖股票)。对于j等于0的情况,buy[0][0]等于-prices[0],当 i > 0 的时候,buy[i][0] = -prices[i] 和 buy[i - 1][0]的最大值。对于sell而言,sell[0][0]设置为0。因为一开始无法操作,所以那些非法状态全部赋值为一个很小的负数。至少比-prices[0]的可能最小值要小。
  5. 对于其他状态,对于buy[i][j],要么从sell[i - 1][j] - prices[i] 转移过来,要么从buy[i - 1][j]转移过来,两者取最大值即可,对于sell[i][j],要么从sell[i - 1][j]转移过来,要么从buy[i - 1][j - 1] + prices[i]转移过来,两者取最大值。
  6. 最终取sells[i]l的最大值。

9、最大正方形

9.1 题目链接

点击跳转到题目位置

9.2 题目描述

在一个由 ‘0’ 和 ‘1’ 组成的二维矩阵内,找到只包含 ‘1’ 的最大正方形,并返回其面积。

提示:

  • m == matrix.length
  • n == matrix[i].length
  • 1 <= m, n <= 300
  • matrix[i][j] 为 ‘0’ 或 ‘1’

9.3 解题代码

class Solution {
    public int maximalSquare(char[][] matrix) {
        int m = matrix.length;
        int n = matrix[0].length;
        int[][] dp = new int[m][n]; // 以i, j为右下角的最大正方形的边长
        for(int i = 0; i < m; ++i){
            for(int j = 0; j < n; ++j){
                if(i == 0 || j == 0){
                    dp[i][j] = matrix[i][j] - '0';
                } else{
                    if(matrix[i][j] == '1'){
                        if(dp[i - 1][j] == dp[i][j - 1]){
                            int len = dp[i - 1][j];
                            dp[i][j] = matrix[i - len][j - len] == '1' ? len + 1 : len;
                        } else{
                            dp[i][j] = 1 + Math.min(dp[i - 1][j], dp[i][j - 1]);
                        }
                    } 
                }  
            }
        }
        int ans = 0;
        for(int i = 0; i < m; ++i){
            for(int j = 0; j < n; ++j){
                ans = Math.max(ans, dp[i][j]);
            }
        }
        return ans * ans;
    }
}

9.4 解题思路

  1. 动态规划,dp[i][j]表示以(i,j)为右下角的最大正方形的边长。
  2. 如果i == 0 或者 j == 0,则matrix[i][j] == ‘1’ 则 dp[i][j] == 1,否则dp[i][j] == 0.
  3. 如果i 和 j 都不等于 0,则看dp[i - 1][j] 和 dp[i - 1][j] 两者是否相等,如果相等,则记len = dp[i - 1][j],此时需要判断matrix[i - len][j - len]是否为‘1’,是的话,最大边长为len + 1,否则为len - 1;如果两者不相等,则为两者的最小值 + 1.

你可能感兴趣的:(面试经典150题,面试,动态规划)