栏目分类:
子分类:
返回
名师互学网用户登录
快速导航关闭
当前搜索
当前分类
子分类
实用工具
热门搜索
名师互学网 > IT > 软件开发 > 后端开发 > Java

leetcode之动态规划刷题总结4

Java 更新时间: 发布时间: IT归档 最新发布 模块sitemap 名妆网 法律咨询 聚返吧 英语巴士网 伯小乐 网商动力

leetcode之动态规划刷题总结4

leetcode之动态规划刷题总结4

1-最长回文子序列
题目链接:题目链接戳这里!!!

思路:动态规划
dp[i][j]:表示从下标i到下标j的最大回文序列
若s[i]==s[j]
递推式如下:
dp[i][j] = dp[i+1][j-1]+2 ;
若s[i]!=s[j]
递推式如下:
dp[i][j] = Math.max(dp[i+1][j],dp[i][j-1]) ;

class Solution {
    public int longestPalindromeSubseq(String s) {
        int n = s.length() ;
        int [][] dp = new int [n][n] ;
        for(int i=0; i=0; i--){
            for(int j=i+1; j 


2-一和零
题目链接:题目链接戳这里!!!

思路:背包问题的变形题
这道题和经典的背包问题非常相似,但是和经典的背包问题只有一种容量不同,这道题有两种容量,即选取的字符串子集中的 0 和 1的数量上限。

经典的背包问题可以使用二维动态规划求解,两个维度分别是物品和容量。这道题有两种容量,因此需要使用三维动态规划求解,三个维度分别是字符串、0 的容量和 1 的容量。

定义三维数组 dp,其中dp[i][j][k] 表示在前 i 个字符串中,使用 j 个 0 和 k 个 1 的情况下最多可以得到的字符串数量。

状态转移方程如下:

class Solution {
    public int findMaxForm(String[] strs, int m, int n) {
        int len = strs.length ;
        int [][][] dp = new int [len+1][m+1][n+1] ;
        for(int i=1; i<=len; i++){
            int [] zero =count(strs[i-1]) ;
            int zeros = zero[0], ones = zero[1] ;
        for(int j=0; j<=m; j++){
            for(int k=0; k<=n; k++){
                if(j>=zeros && k>=ones){
                    dp[i][j][k] = Math.max(dp[i-1][j][k],dp[i-1][j-zeros][k-ones]+1) ;
                }else{
                    dp[i][j][k] = dp[i-1][j][k] ;
                }
            }
        }
        }
              return dp[len][m][n] ;
    }
    public int [] count(String s){
        int [] zeros = new int [2] ;
        for(int i=0; i 


3-使用最小花费爬楼梯
题目链接:题目链接戳这里!!!

思路:dp[i]:表示爬到当前i位置所需要的最小花费
dp[0]=0
dp[1]=0
递推表达式:
dp[i] = Math.min(dp[i-1]+cost[i-1],dp[i-2]+cost[i-2]) ;

class Solution {
    public int minCostClimbingStairs(int[] cost) {
        int [] dp = new int [cost.length+1] ;
        for(int i=2; i<=cost.length; i++){
            dp[i] = Math.min(dp[i-1]+cost[i-1],dp[i-2]+cost[i-2]) ;
        }
        return dp[cost.length];
    }
}


4-零钱兑换II
题目链接:题目链接戳这里!!!

思路:用 dp[x] 表示金额之和等于 x的硬币组合数,目标是求 dp[amount]。动态规划的边界是dp[0]=1。只有当不选取任何硬币时,金额之和才为 0,因此只有 1 种硬币组合。

class Solution {
    public int change(int amount, int[] coins) {
      int [] dp = new int [amount+1] ;
      dp[0] = 1 ;
      for(int coin : coins){
          for(int i=coin; i<=amount; i++){
              dp[i] += dp[i-coin] ;
          }
      }
      return dp[amount] ;
    }
}


5-优美的排列
题目链接:题目链接戳这里!!!

思路1:dfs+标记
每轮搜索,只要当前数字和下标相互可以取余,则累加排列数,继续搜索。每次搜索需要标记已经搜索过的数字。

class Solution {
    public int countArrangement(int n) {
        return dfs(n,1,new boolean[n+1]) ;
    }
    public int dfs(int n, int i, boolean [] vis){
        if(i>n){
            return 1 ;
        }
        int ans = 0 ;
        for(int num=1; num<=n; num++){
            if(!vis[num] &&(i%num==0 || num%i==0)){
                vis[num] = true ;
                ans += dfs(n,i+1,vis) ;
                vis[num] = false ;
            }
        }
        return ans ;
    }
}


思路2:回溯法
我们可以使用回溯法解决本题,从左向右依次向目标排列中放入数即可。

具体地,我们定义函数backtrack(i,n),表示尝试向位置 i放入数。其中 n 表示排列的长度。在当前函数中,我们首先找到一个符合条件的未被使用过的数,然后递归地执行 backtrack(i+1,n),当该函数执行完毕,回溯到当前层,我们再尝试下一个符合条件的未被使用过的数即可。

回溯过程中,我们可以用vis 数组标记哪些数被使用过,每次我们选中一个数 idx,我们就将vis[idx] 标记为 true,回溯完成后,我们再将其置为false。

特别地,为了优化回溯效率,我们可以预处理每个位置的符合条件的数有哪些,用数组 match 保存。当我们尝试向位置 i 放入数时,我们只需要遍历 match[i] 即可。

class Solution {
    List [] match ;
    boolean [] vis ;
    int num ;
    public int countArrangement(int n) {
    match = new List[n+1] ;
    vis = new boolean[n+1] ;
    for(int i=1; i<=n; i++){
        match[i] = new ArrayList<>() ;
    }
    for(int i=1; i<=n; i++){
        for(int j=1; j<=n; j++){
            if(i%j==0 || j%i==0){
                match[i].add(j) ;
            }
        }
    }
    backtrack(1,n) ;
    return num ;
}
public void backtrack(int i, int n){
    if(i==n+1){
        num++ ;
        return ;
    }
    for(int idx : match[i]){
        if(!vis[idx]){
        vis[idx] = true ;
        backtrack(i+1,n) ;
        vis[idx] = false ;
        }
    }
}
}


6-出界的路径数
题目链接:题目链接戳这里!!!

思路:搜索+标记
每轮沿着四个方向搜索,能出界就累加1,步数为0时,不能出界,返回0,搜索完成需要标记当前位置在某一步数下已经搜索的答案,下轮搜索遇到时,直接返回即可。

class Solution {
    int [] dx = {-1,1,0,0} ;
    int [] dy = {0,0,-1,1} ;
    int [][][]cache ;
    int mod = 1000000007;
    public int findPaths(int m, int n, int maxMove, int startRow, int startColumn) {
      
        cache = new int [m][n][maxMove+1] ;
        for(int i=0; i=m || y>=n){
            return 1 ;
        }
        if(maxMove==0){
            return 0 ;
        }
        if(cache[x][y][maxMove]!=-1){
            return cache[x][y][maxMove] ;
        }
        int ans = 0 ;
        for(int i=0; i<4; i++){
            int tx = x + dx[i] ;
            int ty = y + dy[i] ;
            ans += dfs(tx,ty,maxMove-1,m,n) ;
            ans = ans % mod ;
        }
        cache[x][y][maxMove] = ans ;
        return ans ;
    }
}


思路:动态规划
动态规划的状态由移动次数、行和列决定,定义 dp[i][j][k] 表示球移动 i次之后位于坐标 (j,k) 的路径数量。

class Solution {
    int [] dx = {-1,1,0,0} ;
    int [] dy = {0,0,-1,1} ;
    int mod = 1000000007 ;
    public int findPaths(int m, int n, int maxMove, int startRow, int startColumn) {
        int [][][] dp = new int [maxMove+1][m][n] ;
        dp[0][startRow][startColumn] = 1;
        int sum = 0 ;
        for(int i=0; i 0){
                        for(int x=0; x<4; x++){
                            int tx = j + dx[x] ;
                            int ty = k + dy[x] ;
                            if(tx>=0 && tx=0 && ty 

7-两个字符串的删除操作
题目链接:题目链接戳这里!!!

思路:dp[i][j]表示以i-1的字符串word1,以j-1结尾的字符串word2要达到相等,所需删除的元素的最少个数。

若word1[i-1]等于word2[j-1]则不需要删除,递推式如下:
dp[i][j] = dp[i-1][j-1] ;
若word1[i-1]不等于word2[j-1],则需要删除,分为三种可能,递推式如下:
dp[i][j] = Math.min(dp[i-1][j]+1,Math.min(dp[i][j-1]+1,dp[i-1][j-1]+2)) ;

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


8-回文子串
题目链接:题目链接戳这里!!!

思路:dp[j][i]表示从位置j到位置i是否是回文串。
如果s.charAt(i)==s.charAt(j)
且(i-j<2 || dp[j+1][i-1])
则说明一定是回文串。

class Solution {
    public int countSubstrings(String s) {
        boolean [][] dp = new boolean [s.length()][s.length()] ;
        int cnt = 0 ;
    
        for(int i=0; i 


9-最长递增子序列的个数
题目链接:题目链接戳这里!!!
确定dp数组以及下标的含义
这道题目我们要一起维护两个数组。

dp[i]:i之前(包括i)最长递增子序列的长度为dp[i]

cnt[i]:以nums[i]为结尾的字符串,最长递增子序列的个数为cnt[i]

确定递推公式
在最长上升子序列中,我们给出的状态转移是:

if (nums[i] > nums[j]) dp[i] = max(dp[i], dp[j] + 1);

即:位置i的最长递增子序列长度 等于j从0到i-1各个位置的最长升序子序列 + 1的最大值。

本题就没那么简单了,我们要考虑两个维度,一个是dp[i]的更新,一个是cnt[i]的更新。

那么如何更新cnt[i]呢?

以nums[i]为结尾的字符串,最长递增子序列的个数为cnt[i]。

那么在nums[i] > nums[j]前提下,如果在[0, i-1]的范围内,找到了j,使得dp[j] + 1 > dp[i],说明找到了一个更长的递增子序列。

那么以j为结尾的子串的最长递增子序列的个数,就是最新的以i为结尾的子串的最长递增子序列的个数,即:cnt[i] = cnt[j]。

在nums[i] > nums[j]前提下,如果在[0, i-1]的范围内,找到了j,使得dp[j] + 1 == dp[i],说明找到了两个相同长度的递增子序列。

那么以i为结尾的子串的最长递增子序列的个数 就应该加上以j为结尾的子串的最长递增子序列的个数,即:cnt[i] += cnt[j];

class Solution {
    public int findNumberOfLIS(int[] nums) {
        if(nums.length<=1){
            return nums.length ;
        }
        int [] dp = new int [nums.length] ;
        int [] cnt = new int [nums.length] ;
        Arrays.fill(dp,1) ;
        Arrays.fill(cnt,1) ;
        int max = 0 ;
        for(int i=1; inums[j]){
                    if(dp[j]+1 > dp[i]){
                        dp[i] = dp[j]+1 ;
                        cnt[i] = cnt[j] ;
                    }else if(dp[j]+1==dp[i]){
                        cnt[i] += cnt[j] ;
                    }
                }
            }
            max = Math.max(max, dp[i]) ;
        }
        int ans = 0 ;
        for(int i=0; i 


10-只有两个键的键盘
题目链接:题目链接戳这里!!!

思路:1、如果n为一个质数,那么结果就是n,因为最少的情况就是复制一下,然后一个个粘贴。

2、如果n为一个合数,那么他的结果就是分解因式的结果之和。

比如n 为8 ,
那么dp[8] = dp[4]+dp[2]; 因为,8 = 42
dp[4] = dp[2]+dp[2]; 因为,4 = 2
2
由于因为2是一个质数,dp[2] = 2;
于是dp[8] = 6就是我们要求的结果。

class Solution {
    public int minSteps(int n) {
        int [] dp = new int [n+1] ;
        for(int i=2; i<=n; i++){
            dp[i] = i ;
            for(int j=2;j<=Math.sqrt(i);j++){
                if(i%j==0){
                    dp[i] = dp[j] + dp[i/j] ;
                    break ;
                }
            }
        }
        return dp[n];
    }
}

千里之行始于足下,慢慢来,会很快!!!
转载请注明:文章转载自 www.mshxw.com
本文地址:https://www.mshxw.com/it/735308.html
我们一直用心在做
关于我们 文章归档 网站地图 联系我们

版权所有 (c)2021-2022 MSHXW.COM

ICP备案号:晋ICP备2021003244-6号