0
点赞
收藏
分享

微信扫一扫

leetcode之动态规划刷题总结3

Star英 2022-02-11 阅读 63

leetcode之动态规划刷题总结3

1-打家劫舍
题目链接:题目链接戳这里!!!

思路:这题思路不难,但是需要注意细节。
递推式:dp[i]=max(dp[i-1],dp[i-2]+nums[i])

class Solution {
    public int rob(int[] nums) {
        if(nums.length==1){
            return nums[0] ;
        }
        if(nums.length==2){
            return Math.max(nums[0],nums[1]) ;
        }
        int [] dp = new int [nums.length] ;
        dp[0] = nums[0] ;
        dp[1] = Math.max(nums[0],nums[1]) ;
       for(int i=2; i<nums.length; i++){
           dp[i] = Math.max(dp[i-1], nums[i]+dp[i-2]) ;
       }
       return dp[nums.length-1];
    }
}

在这里插入图片描述
2-打家劫舍II
题目链接:题目链接戳这里!!!

思路:这题与第一题的区别在于防盗系统变成一个环了,递推式还是不变的,只不过第一个和最后一个只能选其一。

class Solution {
    public int rob(int[] nums) {
        if(nums.length==1){
            return nums[0] ;
        }
        if(nums.length==2){
            return Math.max(nums[0],nums[1]) ;
        }
        return Math.max(f(nums,0,nums.length-1),f(nums,1,nums.length)) ;
    }
    public int f(int []nums, int begin, int end){
        int [] dp = new int [end-begin+1] ;
        dp[begin] = nums[begin];
        dp[begin+1] = Math.max(nums[begin],nums[begin+1]) ;
        for(int i=begin+2; i<end; i++){
            dp[i] = Math.max(dp[i-1],dp[i-2]+nums[i]) ;
        }
        return dp[end-1] ;
    }
}

在这里插入图片描述
3-比特位计数
题目链接:题目链接戳这里!!!

思路:主要式递推式找到,如下所示:
dp[2i] = dp[i] ;
dp[2
i+1] = dp[i]+1;

class Solution {
    public int[] countBits(int n) {
        if(n==0){
            return new int []{0} ;
        }
        int [] dp = new int [n+1] ;
        dp[0] = 0 ;
        dp[1] = 1 ;
        for(int i=0; i<=n/2; i++){
           dp[2*i] = dp[i] ;
           if(2*i+1<=n)
           dp[2*i+1] = dp[i]+1;
        }
        return dp ;
    }
}

在这里插入图片描述
4-最佳买卖股票时期含冷冻期
题目链接:题目链接戳这里!!!

思路:sell[i]表示截至第i天,最后一个操作是卖时的最大收益;
buy[i]表示截至第i天,最后一个操作是买时的最大收益;
cool[i]表示截至第i天,最后一个操作是冷冻期时的最大收益;
递推公式:
sell[i] = max(buy[i-1]+prices[i], sell[i-1]) (第一项表示第i天卖出,第二项表示第i天冷冻)
buy[i] = max(cool[i-1]-prices[i], buy[i-1]) (第一项表示第i天买进,第二项表示第i天冷冻)
cool[i] = max(sell[i-1], buy[i-1], cool[i-1])

class Solution {
    public int maxProfit(int[] prices) {
        if(prices.length==1){
            return 0 ;
        }
   
       int [] sale = new int [prices.length] ;
       int [] buy = new int [prices.length] ;
       int [] coll = new int [prices.length] ;
       buy[0] = -prices[0] ;
       for(int i=1;i<prices.length; i++){
           sale[i] = Math.max(sale[i-1],buy[i-1]+prices[i]) ;
           buy[i] = Math.max(buy[i-1],coll[i-1]-prices[i]) ;
           coll[i] = Math.max(Math.max(coll[i-1],buy[i-1]),sale[i-1]) ;
       }
       return sale[prices.length-1] ;
    }
}

在这里插入图片描述
思路2:
f[i][0]:代表持有股票的最大利润
f[i][1]:代表不持有股票,但是处于冷冻期的最大利润
f[i][2]:代表不持有股票,且不处于冷冻期的最大利润

class Solution {
    public int maxProfit(int[] prices) {
        if(prices.length==1){
            return 0 ;
        }
        int [][] f = new int [prices.length][3] ;
        f[0][0] = -prices[0] ;
       for(int i=1;i<prices.length; i++){
         f[i][0] = Math.max(f[i-1][0],f[i-1][2]-prices[i]) ;
         f[i][1] = f[i-1][0] + prices[i] ;
         f[i][2] = Math.max(f[i-1][2],f[i-1][1]) ;
       }
       return Math.max(f[prices.length-1][1],f[prices.length-1][2]) ;
    }
}

在这里插入图片描述
5-为运算表达式设计优先级
题目链接:题目链接戳这里!!!
思路:分治法:如果当前字符串全部为数字,则返回数字即可,不需要运算。
否则需要找出当前的符号,递归计算左半部分和右半部分,再根据符号计算最终结果。

class Solution {
    public List<Integer> diffWaysToCompute(String expression) {
        List<Integer> list = new ArrayList<>() ;
        if(isDigits(expression)){
            list.add(Integer.parseInt(expression)) ;
            return list ;
        }
        for(int i=0; i<expression.length(); i++){
            if(expression.charAt(i)=='+' || expression.charAt(i)=='-' || expression.charAt(i)=='*'){
                List<Integer> left = diffWaysToCompute(expression.substring(0,i)) ;
                List<Integer> right = diffWaysToCompute(expression.substring(i+1)) ;
                char op = expression.charAt(i) ;
                for(int j : left){
                    for(int k : right){
                        if(op=='+'){
                            list.add(j+k) ;
                        }else if(op=='-'){
                            list.add(j-k) ;
                        }else if(op=='*'){
                            list.add(j*k) ;
                        }
                    }
                }
            }
        }
        return list ;

    }
    public boolean isDigits(String expression){
        for(int i=0; i<expression.length(); i++){
            if(!Character.isDigit(expression.charAt(i))){
                return false ;
            } 
        }
         return true ;
    }
}

在这里插入图片描述

6-整数拆分
题目链接:题目链接戳这里!!!

思路:动态规划
dp[i]:表示将正整数i拆分成至少两个正整数和之后,这些正整数的最大乘积。

有两种情况:
第一种,i拆分成i和i-j并且i-j不继续拆分,则当前值为j*(i-j)
第二种,i拆分成i和i-j并且i-j继续拆分,则当前值为j*dp[i-j]

每一轮找出第j个位置拆分的最大值即可。

dp[n]表示将正整数n拆分成至少两个正整数和之后,这些正整数的最大乘积。

class Solution {
    public int integerBreak(int n) {
        int [] dp = new int [n+1] ;
        for(int i=2; i<=n; i++){
            int curMax = 0;
            for(int j=1; j<i; j++){
                curMax = Math.max(curMax,Math.max(j*(i-j),j*dp[i-j])) ;
            }
            dp[i] = curMax ;
        }
        return dp[n] ;
    }
}

在这里插入图片描述
7-计算各个位数不同的数字个数
题目链接:题目链接戳这里!!!

思路1:递归法。
所有位不相同
即第一位9种选择
第二位9种选择(因为多一个0,所有有9种)
第三位8种…接下来依次递减
所有n位数有多少个不同即可获得
在返回count加上小他一位的即可
return count+countNumbersWithUniqueDigits(n-1);

class Solution {
    public int countNumbersWithUniqueDigits(int n) {
        if(n==0){
            return 1 ;
        }
        if(n==1){
            return 10 ;
        }
        int j = 9 ;
        int cnt = 9 ;
        for(int i=1;i<n; i++){
            cnt *= j ;
            j -- ;
        }
        return countNumbersWithUniqueDigits(n-1) + cnt ;
    }
}

在这里插入图片描述
思路2:动态规划

class Solution {
    public int countNumbersWithUniqueDigits(int n) {
        if(n==0){
            return 1;
        }
      int [] dp = new int [n+1] ;
      dp[0] = 1 ;
      dp[1] = 10;
      for(int i=2; i<=n; i++){
          dp[i] = dp[i-1] + (dp[i-1]-dp[i-2])*(10-(i-1));
      }
      return dp[n] ;
    }
}

在这里插入图片描述
8-判断子序列
题目链接:题目链接戳这里!!!

思路1:双指针法。

class Solution {
    public boolean isSubsequence(String s, String t) {
        int ls = 0, lt = 0 ;
        while(ls<s.length() && lt<t.length()){
            if(s.charAt(ls)==t.charAt(lt)){
                ls ++ ;
            }
            lt ++ ;
        }
        return ls==s.length() ;
    }
}

在这里插入图片描述
思路2:调库法,哈哈哈

class Solution {
    public boolean isSubsequence(String s, String t) {
      int idx = -1 ;
      for(char c : s.toCharArray()){
          idx = t.indexOf(c,idx+1) ;
          if(idx==-1){
              return false ;
          }
      }
      return true ;
    }
}

在这里插入图片描述
9-等差数列划分
题目链接:题目链接戳这里!!!

思路:规律题。
这道题主要是需要找到其规律,从小的例子出发,仔细观察,会发现当整个数组为(1, 2, 3, 4, 5, 6)的时候,我们先取出前三个,(1, 2, 3)的等差数列的个数为1,(1, 2, 3, 4)的等差数列的个数为3,(1, 2, 3, 4, 5)的等差数列的个数为6,(1, 2, 3, 4, 5, 6)的等差数列个数为10,以此类推我们可以很容易的发现在一个等差数列中加入一个数字,如果还保持着等差数列的特性,每次的增量都会加1,如果刚加进来的数字与原先的序列构不成等差数列,就将增量置为0,接下来继续循环,执行以上的逻辑即可.可以发现,这道题只要找到规律还是相当的简单。

class Solution {
    public int numberOfArithmeticSlices(int[] nums) {
       
        int add = 0 ;
        int res = 0 ;
        for(int i=2; i<nums.length; i++){
            if(nums[i]-nums[i-1]==nums[i-1]-nums[i-2]){
                res += (++add) ;
            }else{
                add = 0 ;
            }
        }
        return res ;
    }
}

在这里插入图片描述
10-分割等和子集
题目链接:题目链接戳这里!!!

思路:这是典型的0-1背包的变形题,我们其实要求的就是从集合中取出一些数字,求和等于所有集合元素和的一半,则返回为true,否则返回为false .这道题与传统的「0-1 背包问题」的区别在于,传统的「0-−1 背包问题」要求选取的物品的重量之和不能超过背包的总容量,这道题则要求选取的数字的和恰好等于整个数组的元素和的一半。

根据数组的长度 n判断数组是否可以被划分。如果 n<2,则不可能将数组分割成元素和相等的两个子集,因此直接返回 false。

计算整个数组的元素和sum 以及最大元素 max,若sum是奇数,则肯定不可能将数组分割成两个相等的子集,返回false,如果最大值max大于sum/2,则返回false .

接下来定义dp数组,dp[i][j]表示从数组的 [0,i]下标范围内选取若干个正整数(可以是 0 个),是否存在一种选取方案使得被选取的正整数的和等于 j。初始时,dp 中的全部元素都是 false。

初始化dp数组
dp[i][0]=true
dp[0][nums[0]]=true

递推表达式如下:
在这里插入图片描述

class Solution {
    public boolean canPartition(int[] nums) {
        int n = nums.length ;
        if(n<2){
            return false ;
        }
        int max = 0,sum=0 ;
        for(int i=0; i<n; i++){
            max = Math.max(max,nums[i]) ;
            sum += nums[i] ;
        }
        int target = sum / 2 ;
        if(sum%2!=0 || max>target){
            return false ;
        }
        boolean [][] dp = new boolean [n][target+1] ;
        dp[0][nums[0]] = true ;
        for(int i=0; i<n; i++){
            dp[i][0] = true ;
        }
        for(int i=1; i<n; i++){
            int num = nums[i] ;
            for(int j=1; j<=target; j++){
                if(j>=num){
                    dp[i][j] = (dp[i-1][j] | dp[i-1][j-num]) ;
                }else{
                    dp[i][j] = dp[i-1][j] ;
                }
            }
        }
        return dp[n-1][target] ;
        
    }
}

在这里插入图片描述

举报

相关推荐

0 条评论