动态规划(二维动态规划) 动态规划专题

18.从递归入手二维动态规划

前置知识: 讲解038-经典递归过程解析、讲解066-从递归入手一维动态规划

本节课: 讲解从递归到二维动态规划的过程 讲解二维动态规划的空间压缩技巧 讲解哪些递归不适合或者说没有必要改成动态规划

下节课:直接从动态规划的定义入手,来见识更多二维动态规划问题

注意: 二维动态规划问题非常多,不仅讲解067、讲解068涉及,整个系列课程会大量涉及 【必备】课程后续会讲背包dp、区间dp、状压dp等等,依然包含大量二维动态规划问题

尝试函数有1个可变参数可以完全决定返回值,进而可以改出1维动态规划表的实现 同理 尝试函数有2个可变参数可以完全决定返回值,那么就可以改出2维动态规划的实现

一维、二维、三维甚至多维动态规划问题,大体过程都是: 写出尝试递归 记忆化搜索(从顶到底的动态规划) 严格位置依赖的动态规划(从底到顶的动态规划) 空间、时间的更多优化

64. 最小路径和

已解答

中等

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

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

示例 1:

img

输入:grid = [[1,3,1],[1,5,1],[4,2,1]]
输出:7
解释:因为路径 1→3→1→1→1 的总和最小。
static int [][] dp =  new int[200][200];
public int minPathSum(int[][] grid) {
    int n = grid.length;
    int m = grid[0].length;
    dp[n-1][m-1] = grid[n-1][m-1];
    for(int c = m-2;c>=0;c--){
        dp[n-1][c] =grid[n-1][c] + dp[n-1][c+1] ;
    }
    for(int r = n-2;r>=0;r--){
        dp[r][m-1] = grid[r][m-1]+dp[r+1][m-1];
    }
    for(int r = n-2;r>=0;r--){
        for(int c = m-2;c>=0;c--){
            dp[r][c] = grid[r][c]+Math.min(dp[r+1][c],dp[r][c+1]);
        }
    }
    return dp[0][0];
}

能改成动态规划的递归,统一特征:

决定返回值的可变参数类型往往都比较简单,一般不会比int类型更复杂。为什么?

从这个角度,可以解释 带路径的递归(可变参数类型复杂),不适合或者说没有必要改成动态规划 题目2就是说明这一点的

不管几维动态规划 经常从递归的定义出发,避免后续进行很多边界讨论 这需要一定的经验来预知

1143. 最长公共子序列

已解答

中等

提示

给定两个字符串 text1text2,返回这两个字符串的最长 公共子序列 的长度。如果不存在 公共子序列 ,返回 0

一个字符串的 子序列 是指这样一个新的字符串:它是由原字符串在不改变字符的相对顺序的情况下删除某些字符(也可以不删除任何字符)后组成的新字符串。

  • 例如,"ace""abcde" 的子序列,但 "aec" 不是 "abcde" 的子序列。

两个字符串的 公共子序列 是这两个字符串所共同拥有的子序列。

示例 1:

输入:text1 = "abcde", text2 = "ace" 
输出:3  
解释:最长公共子序列是 "ace" ,它的长度为 3 。
static int[][] dp;
public static int longestCommonSubsequence(String text1, String text2) {
    char [] s1 = text1.toCharArray();
    char [] s2 = text2.toCharArray();
    int n = s1.length;
    int m = s2.length;
    int [][]dp = new int[n+1][m+1];
    if (s1[0]==s2[0]){
        dp[0][0]=1;
    }
    for (int i = 1;i<n;i++){
        if (s1[i]==s2[0]){
            dp[i][0]=1;
        }else {
            dp[i][0]=dp[i-1][0];
        }
    }
    for (int i = 1;i<m;i++){
        if (s2[i]==s1[0]){
            dp[0][i]=1;
        }else {
            dp[0][i]=dp[0][i-1];
        }
    }
    for (int row = 1;row<n;row++){
        for (int col = 1;col<m;col++){
            int p1 = dp[row-1][col-1];
            int p2 = 0;
            int p3 = 0;
            if(s1[row]==s2[col]){
                p1 += 1;
            }else {
                p2 = dp[row-1][col];
                p3 = dp[row][col-1];
            }
            dp[row][col] = Math.max(p1,Math.max(p2,p3));
        }
    }
    return dp[n-1][m-1];
}

516. 最长回文子序列

已解答

中等

给你一个字符串 s ,找出其中最长的回文子序列,并返回该序列的长度。

子序列定义为:不改变剩余字符顺序的情况下,删除某些字符或者不删除任何字符形成的一个序列。

示例 1:

输入:s = "bbbab"
输出:4
解释:一个可能的最长回文子序列为 "bbbb" 。
public static int longestPalindromeSubseq(String s) {
    char [] chars = s.toCharArray();
    int l = 0;
    int r = s.length()-1;
    int [][]dp = new int[r+1][r+1];
    for(int i = 0;i<=r;i++){
        dp[i][i] = 1 ;
    }
    for(int row = r-1;row>=0;row--){
        for(int col = row+1;col<r+1;col++){
            //当前两种情况 首位字符是否相等
            if(chars[row]==chars[col]){
                dp[row][col] = 2+dp[row+1][col-1];
            }else{
                int p1 = Math.max(dp[row+1][col],dp[row][col-1]);
                dp[row][col] = p1;
            }
        }
    }
    return dp[0][r];
}

329. 矩阵中的最长递增路径

已解答

困难

给定一个 m x n 整数矩阵 matrix ,找出其中 最长递增路径 的长度。

对于每个单元格,你可以往上,下,左,右四个方向移动。 你 不能对角线 方向上移动或移动到 边界外(即不允许环绕)。

示例 1:

img

输入:matrix = [[9,9,4],[6,6,8],[2,1,1]]
输出:4 
解释:最长递增路径为 [1, 2, 6, 9]。
static int[][] dp ;
static int [] move = {1,0,-1,0,1};
static int n;
static int m;
public static int longestIncreasingPath(int[][] matrix) {
    n = matrix.length;
    m = matrix[0].length;
    int max = 0;
    dp = new int[n][m];
    for(int i = 0;i<n;i++){
        Arrays.fill(dp[i],-1);
    }
    for(int i = 0;i<n;i++){
        for(int j = 0;j<m;j++){
            max = Math.max(max,process(matrix,i,j));
        }
    }
    return max;
}
public static int process(int [][]matrix,int row ,int col){
    if(dp[row][col]!=-1){
        return dp[row][col];
    }
    int ans = 1;
    for(int i = 1;i<move.length;i++){
        int nrow = row+move[i-1];
        int ncol = col+move[i];
        if(nrow>=0&&ncol>=0&&ncol<m&&nrow<n&&matrix[nrow][ncol]>matrix[row][col]){
            ans = Math.max(ans,process(matrix,nrow,ncol)+1);
        }
    }
    dp[row][col] = ans;
    return ans;
}

二叉树

  • 题目

  • 题解(2)

  • 讨论(16)

  • 排行

时间限制:1秒 空间限制:128M

知识点Java工程师C++工程师PHP工程师golang工程师前端工程师安卓工程师iOS工程师算法工程师大数据开发工程师信息技术岗运维工程师安全工程师数据分析师数据库工程师游戏研发工程师区块链测试开发工程师测试工程师阿里巴巴2021

warning

校招时部分企业笔试将禁止编程题跳出页面,为提前适应,练习时请使用在线自测,而非本地IDE。

描述

小强现在有𝑛n个节点,他想请你帮他计算出有多少种不同的二叉树满足节点个数为𝑛n且树的高度不超过𝑚m的方案.因为答案很大,所以答案需要模上1e9+7后输出. 树的高度: 定义为所有叶子到根路径上节点个数的最大值.

例如: 当n=3,m=3时,有如下5种方案:

img

img

img

img

img

数据范围:1≤𝑛,𝑚≤50 1≤n,m≤50

进阶:时间复杂度𝑂(𝑚𝑛2) O(m**n2) ,空间复杂度𝑂(𝑛𝑚) O(n**m)

输入描述:

第一行输入两个正整数𝑛n和𝑚m. 1≤𝑚≤𝑛≤501≤mn≤50

输出描述:

输出一个答案表示方案数.

示例1

输入:

3 3

复制

输出:

5
import java.util.Arrays;
import java.util.Scanner;
import java.io.*;
​
// 注意类名必须为 Main, 不要有任何 package xxx 信息
public class Main {
     static int MOD = 1000000007;
    static int [][] dp;
    public static void main(String[] args)throws IOException {
        BufferedReader bf = new BufferedReader( new InputStreamReader(System.in));
        // 注意 hasNext 和 hasNextLine 的区别
        String [] s = bf.readLine().split(" ");
        int n = Integer.parseInt(s[0]);
        int m = Integer.parseInt(s[1]);
        dp = new int [n+1][m+1];
        for (int i = 0;i<=m;i++){
            dp[0][i] = 1;
        }
        for (int row = 1;row<n+1;row++){
            for (int col = 1;col<m+1;col++){
                long ans = 0;
                for (int k = 0; k < n+1; k++) {
                    if(row-k-1<0){
                        break;
                    }
                    ans = (ans + ((long) dp[k][col - 1] * dp[row - k - 1][ col - 1]) % MOD) % MOD;
                }
                dp[row][col] = (int) ans;
            }
        }
        PrintWriter out = new PrintWriter(new OutputStreamWriter(System.out));
        out.print(dp[n][m]);
        out.close();
    }
    /**返回高度为m,节点数为n的二叉树有多少种 */
    public static int process(int n,int m){
        //如果此时不剩下节点,说明得到一种
        if(n==0){
            return 1;
        }
        if(m==0){
            return 0;
        }
        if(dp[n][m]!=-1){
            return dp[n][m];
        }
        long ans = 0;
        // n个点,头占掉1个
        for (int k = 0; k < n; k++) {
            // 一共n个节点,头节点已经占用了1个名额
            // 如果左树占用k个,那么右树就占用i-k-1个
            ans = (ans + ((long) process(k, m - 1) * process(n - k - 1, m - 1)) % MOD) % MOD;
        }
        dp[n][m] =(int) ans;
        return  (int) ans;
    }
}

前置知识: 从递归入手二维动态规划

本节课不再从递归入手,而是直接从动态规划的定义入手,来见识更多二维动态规划问题

本节课包含一些 比较巧妙的尝试思路

注意: 二维动态规划问题非常多,整个系列课程会大量涉及 【必备】课程后续会讲背包dp、区间dp、状压dp等等,依然包含大量二维动态规划问题

115. 不同的子序列

已解答

困难

给你两个字符串 st ,统计并返回在 s子序列t 出现的个数,结果需要对 109 + 7 取模。

示例 1:

输入:s = "rabbbit", t = "rabbit"
输出:3
解释:
如下所示, 有 3 种可以从 s 中得到 "rabbit" 的方案。
rabbbit
rabbbit
rabbbit
static int[][] dp  ;
​
public static int numDistinct(String s, String t) {
    //定义转移方程 dp[i][j]表示 在到s[i]位置之前所有的子序列满足t的前j(不包含j)个字符的数量
    char[] chars = s.toCharArray();
    char[] chars1 = t.toCharArray();
    int n = chars.length;
    int m = t.length();
    dp = new int[n+1][m+1];
    //当 j = 0的时候,默认空串是不是就能解决
    for (int i = 0;i<n+1;i++){
        dp[i][0] = 1;
    }
    //当前 i j位置,分两种情况,如果 s字符串i位置的字符等于t字符串j位置的字符
    //dp[i][j] = dp[i-1][j-1]+dp[i-1][j]
    //如果不等于dp[i][j] = dp[i-1][j]
    for (int i = 1;i<n+1;i++){
        for (int j = 1;j<=i&&j<m+1;j++){
            dp[i][j]+=dp[i-1][j];
            if (chars1[j-1]==chars[i-1]){
                dp[i][j] += dp[i-1][j-1];
            }
        }
    }
    return dp[n][m];
}

72. 编辑距离

已解答

中等

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

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

  • 插入一个字符

  • 删除一个字符

  • 替换一个字符

示例 1:

输入:word1 = "horse", word2 = "ros"
输出:3
解释:
horse -> rorse (将 'h' 替换为 'r')
rorse -> rose (删除 'r')
rose -> ros (删除 'e')
public static int minDistance(String word1, String word2) {
    //dp[i][j]代表 当前使word1 == word2所做的的操作数
    char[] chars1 = word1.toCharArray();
    char[] chars2 = word2.toCharArray();
    int n = chars1.length;
    int m = chars2.length;
    int [][] dp = new int[n+1][m+1];
    //本身就等于dp[i-1][j]+1;
    //if(word1[i-1] == word[j-1])  当前字符就等于 dp[i][j] = dp[i-1][j-1]
    //如果不相等  dp[i][j] = dp[i-1][j-1]+1;
​
    for(int i = 0;i<=n;i++){
        dp[i][0]=i;
    }
    for (int i = 1;i<=m;i++){
        dp[0][i] = i;
    }
    for (int i = 1;i<=n;i++){
        for (int j = 1;j<=m;j++){
            int p1 = dp[i-1][j]+1;
            int p2 = dp[i][j-1]+1;
            if (chars1[i-1]==chars2[j-1]){
                p1 = Math.min( dp[i-1][j-1],p1);
            }else{
                p1 = Math.min(dp[i-1][j-1]+1,p1);
            }
            dp[i][j] = Math.min(p1,p2);
        }
    }
    return dp[n][m];
}

97. 交错字符串

已解答

中等

相关标签

相关企业

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

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

子字符串

  • 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 意味着字符串 ab 连接。

示例 1:

img

输入:s1 = "aabcc", s2 = "dbbca", s3 = "aadbbcbcac"
输出:true
public static boolean isInterleave(String s1, String s2, String s3) {
    int l1 = 0;
    int l2 = 0;
    int l3 = 0;
    int n = s1.length();
    int m = s2.length();
    int q = s3.length();
    char [] chars1 = s1.toCharArray();
    char [] chars2 = s2.toCharArray();
    char [] chars3 = s3.toCharArray();
    if(n+m!=q){
        return false;
    }
    boolean [][] dp = new boolean[q+1][q+1];
    for (int i = 0;i<=q;i++){
        dp[i][q] = true;
    }
    for (int i = q;i>=0;i--){
        for (int j = q-1;j>=0;j--){
            boolean flag1 = false;
            boolean flag2 = false;
            if(i<chars1.length&&chars1[i]==chars3[j]){
                flag1 = dp[i+1][j+1];
            }
            if(j-i>=0&&j-i<chars2.length&&chars2[j-i]==chars3[j]){
                flag2 = dp[i][j+1];
            }
            dp[i][j] = flag1||flag2;
        }
    }
   return dp[0][0];
}

明天更新三维动态规划,持续关注

  • 8
    点赞
  • 20
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
动态规划是一种在数学优化中用于求解最优化问题的方法,常用于解决具有重叠子问题和最优子结构的问题,比如二维空间中的物品装箱问题。在MATLAB中,可以使用这种算法来高效地解决二维空间内如何尽可能多地放置满足大小限制的矩形物体,以达到最小化浪费空间的目的。 具体步骤包括: 1. 定义状态:在这个问题中,状态通常是每个位置(i, j)上能够放下多少个物体,或者能否放下一个特定尺寸的物体。 2. 建立状态转移方程:根据当前位置和物体的尺寸,确定是否能放入,并更新当前位置的状态值,要考虑已经放入的物体对后续放置的影响。 3. 初始化:创建一个二维数组来表示状态,通常开始时所有位置都无法放置物体,状态为0。 4. 动态规划遍历:从左上角开始,使用递推的方式填充这个数组,直到遍历完整个二维空间。 5. 最终状态:在状态数组右下角的元素将给出在给定尺寸约束下能放置的最大物体数量。 6. 反向追踪:找到放置路径,即回溯状态数组,记录每个位置的最优解。 以下是一个简单的MATLAB代码示例,用于解决二维装箱问题: ```matlab function [maxObjects, positions] = twoDimensionalBinPacking(rectangles, maxRectSize) % rectangles: 一个矩形数组,每个元素包含矩形的宽度和高度 % maxRectSize: 可以放入的最大矩形尺寸 % 初始化状态矩阵,所有位置都是未被占用 numRows = size(rectangles, 1); numCols = size(rectangles, 2); dp = zeros(numRows, numCols); % 填充状态矩阵 for i = 1:numRows for j = 1:numCols for rect = rectangles if isInside(rect, [i, j], maxRectSize) && dp(i-rect(1), j-rect(2)) == 0 dp(i, j) = dp(i-rect(1), j-rect(2)) + 1; positions{i, j} = rect; % 保存位置信息 end end end end % 找到最大物品数 maxObjects = max(dp(:)); % 返回最大物品数和位置信息 [~, idx] = max(dp(:)); positions = positions(idx); end % 判断一个矩形是否能完全放入指定位置 function isInside(rect, pos, maxRectSize) return pos(1) >= rect(1) && pos(2) >= rect(2) && pos(1) + rect(1) <= maxRectSize(1) && pos(2) + rect(2) <= maxRectSize(2); end ```
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值