leetcode之动态规划刷题总结4

Posted nuist__NJUPT

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了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<n; i++)
            dp[i][i] = 1 ;
        
        for(int i=n-1; i>=0; i--)
            for(int j=i+1; j<n; j++)
                if(s.charAt(i) != s.charAt(j))
                    dp[i][j] = Math.max(dp[i+1][j],dp[i][j-1]) ;
                else
                    dp[i][j] = dp[i+1][j-1]+2 ;
                
            
        
        return dp[0][n-1] ;
    


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<s.length(); i++)
            zeros[s.charAt(i)-'0']++ ;
        
        return zeros ;
    


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<Integer> [] 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; i++)
            for(int j=0; j<n; j++)
                for(int k=1; k<=maxMove; k++)
                    cache[i][j][k] = -1 ;
                
            
        
        return dfs(startRow,startColumn,maxMove,m,n) ;
    
    public int dfs(int x, int y, int maxMove, int m, int n)
        if(x<0 || y<0 || x>=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<maxMove; i++)
            for(int j=0; j<m; j++)
                for(int k=0; k<n; k++)
                    int count = dp[i][j][k] ;
                    if(count > 0)
                        for(int x=0; x<4; x++)
                            int tx = j + dx[x] ;
                            int ty = k + dy[x] ;
                            if(tx>=0 && tx<m && ty>=0 && ty<n)
                                dp[i+1][tx][ty] = (dp[i+1][tx][ty]+count) % mod ;
                            else
                                sum = (sum+count)%mod ;
                            
                        
                    
                
            
        
        return sum ;
    

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<word1.length()+1; i++)
            dp[i][0] = i ;
        
        for(int j=1; j<word2.length()+1; j++)
            dp[0][j] =  j ;
        
        for(int i=1; i<word1.length()+1; i++)
            for(int j=1; j<word2.length()+1; j++)
                ifleetcode之动态规划刷题总结8

leetcode之动态规划刷题总结6

leetcode之动态规划刷题总结7

leetcode之动态规划刷题总结3

leetcode之动态规划刷题总结2

leetcode之动态规划刷题总结1(Java)