Letcode动态规划专题-初级

最大子数组和

在这里插入图片描述
最大子数组的和

爬楼梯

在这里插入图片描述

杨辉三角《一》《2》

// 1
// 1 1
// 1 2 1
// 1 3 3 1
// 1 4 6 4 1
// 1 5 10 10 5 1
//

// f(i)(j) = f(i-1)f(j-1)+f(i)f(j-1)

class Solution {
    public List<List<Integer>> generate(int numRows) {
        List<List<Integer>> result = new ArrayList<>();
        int[][] array = new int[numRows][numRows];
        for(int i = 0;i<numRows;i++){
            List<Integer> list = new ArrayList<Integer>();
            for(int j = 0;j<=i;j++){
                if(j==0||j==i){
                    array[i][j]=1;
                }else{
                   array[i][j]=array[i-1][j-1]+array[i-1][j];
                }
                list.add(array[i][j]);
            }
            result.add(list);

        }
        return result;
    }
}

// 1
// 1 1
// 1 2 1 
// 1 3 3 1
// 1 4 6 4 1
// 1 5 10 10 5 1 
//

//  f(i)(j) = f(i-1)f(j-1)+f(i)f(j-1)

2、121. 买卖股票的最佳时机

两个动态规划方程


// 动态规划 前i天的最大收益 = max{前i-1天的最大收益,第i天的价格-前i-1天中的最小价格}

class Solution {
    public int maxProfit(int[] prices) {
        int maxProfit =0;
        int minValue = Integer.MAX_VALUE;

        for(int price:prices){
            if(price<minValue){
                minValue = price;
            }else{
                maxProfit = Math.max(price-minValue,maxProfit);
            }
        }
        return maxProfit;
    }
}


状态定义方法:将第i天卖出股票可以获得的最大利润定义为dp[i],然后求解,代码如下:

class Solution {
    public int maxProfit(int[] prices) {
        int len = prices.length;
        int res = 0;
        // 前一天卖出可以获得的最大利润
        int pre = 0;
        for (int i = 1; i < len; i++) {
            // 利润差
            int diff = prices[i] - prices[i - 1];
           // 状态转移方程:第i天卖出可以获得的最大利润 = 第i-1天卖出的最大利润 + 利润差
            pre = Math.max(pre + diff, 0);
            res = Math.max(res, pre);
        }
        return res;
    }
}

在这里插入图片描述

public class Solution {

    public int maxProfit(int[] prices) {
        int len = prices.length;
        // 特殊判断
        if (len < 2) {
            return 0;
        }
        int[][] dp = new int[len][2];

        // dp[i][0] 下标为 i 这天结束的时候,不持股,手上拥有的现金数
        // dp[i][1] 下标为 i 这天结束的时候,持股,手上拥有的现金数

        // 初始化:不持股显然为 0,持股就需要减去第 1 天(下标为 0)的股价
        dp[0][0] = 0;
        dp[0][1] = -prices[0];

        // 从第 2 天开始遍历
        for (int i = 1; i < len; i++) {
            dp[i][0] = Math.max(dp[i - 1][0], dp[i - 1][1] + prices[i]);
            dp[i][1] = Math.max(dp[i - 1][1], -prices[i]);
        }
        return dp[len - 1][0];
    }
}

338. 比特位计数

在这里插入图片描述
1、常规的暴力破解方法

class Solution {
    public int[] countBits(int n) {
        int[] result = new int[n+1];
        for(int i=0;i<=n;i++){
            result[i] = countOnes(i);
        }
        return result;
    }

    public int countOnes(int x){
        int one= 0;
        while(x>0){
            x = x&(x-1);
            one++;
        }
        return one;
    }
}

在这里插入图片描述

2、状态转移方程

class Solution {
    public int[] countBits(int n) {
        int[] dp = new int[n+1];

        for(int i=0;i<=n;i++){
            if (i%2==0) {
                dp[i]=dp[i/2];
            } else {
                dp[i]=dp[i/2]+1;
            }
        }
        return dp;
    }
}

在这里插入图片描述

菲波那切数列-滚动数组

F(0) = 0,F(1) = 1
F(n) = F(n - 1) + F(n - 2),其中 n > 1

class Solution {
    public int fib(int n) {
        if(n<2){
            return n;
        }

        int p = 0;
        int q = 0;
        int r = 1;
        for(int i=2;i<=n;i++){
            p=q;
            q=r;
            r=p+q;
        }
        return r;


    }
}

花费最小的代码爬楼梯

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




//  首先要推递归方程
//  f(0) = 0 ; f(1) =0 
//  f(n) = Min(f(n-1)+cost[n-1],f(n-2)+cost[n-2])

第 N 个泰波那契数

泰波那契序列 Tn 定义如下:

T0 = 0, T1 = 1, T2 = 1, 且在 n >= 0 的条件下 Tn+3 = Tn + Tn+1 + Tn+2

给你整数 n,请返回第 n 个泰波那契数 Tn 的值。

class Solution {
    public int tribonacci(int n) {
        int[] dp = new int[n+1];
        if(n==0){
            return 0;
        }
        if(n==1){
            return 1;
        }
        if(n==2){
            return 1;
        }
        dp[0] =0;
        dp[1] =1;
        dp[2] =1;
        for(int j=3;j<dp.length;j++){
            dp[j] = dp[j-1] + dp[j-2] + dp[j-3];
        }
        return dp[n];
    }
}

获取生成数组中的最大值

在这里插入图片描述

class Solution {
    public int getMaximumGenerated(int n) {
        int[] dp  = new int[n+1];
        
        if(n==0){
            return 0;
        }

        if(n==1){
            return 1;
        }

        int max = 1;
        dp[0]=0;
        dp[1]=1;
        for(int j=2;j<=n;j++){
            if(j%2==0){
                dp[j] =dp[j/2];
            }else{
                dp[j] = dp[j/2]+dp[j/2+1];
            }
            max = (dp[j]>max)?dp[j]:max;
        }
        return max;
    }
}

下载插件

在这里插入图片描述

令 dp[i] 表示下载 i 个插件需要的最少分钟数,则 dp[i] = dp[(i + 1) / 2] + 1;

在这里插入图片描述

剑指 Offer 10- II. 青蛙跳台阶问题

在这里插入图片描述

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

// 动态规划方程
// f(0)=0
// f(1)=1
// f(2)=2
// f(n)=f(n-1)+f(n-2)

连续子数组的最大和


class Solution {
    public int maxSubArray(int[] nums) {
        int size = nums.length;
        int[] dp = new int[size];
        int max = nums[0];
        dp[0] = nums[0];
        for(int i=1;i<size;i++){
            dp[i] = Math.max(dp[i-1]+nums[i],nums[i]);
            max = dp[i]>max?dp[i]:max;
        }
        return max;
    }
}



// 连续子数组的最大和 
//      Max(f(i-1)+cost[i],cost[i])

面试题 08.01. 三步问题

在这里插入图片描述

class Solution {
    public int waysToStep(int n) {
        if(n==1){
            return 1;
        }

        if(n==2){
            return 2;
        }

        if(n==3){
            return 4;
        }

        int[] dp  = new int[n+1];
        dp[1]=1;
        dp[2]=2;
        dp[3]=4;
        for(int i=4;i<=n;i++){
            dp[i] = ((dp[i-1]+dp[i-2])%(1000000007)+dp[i-3])%(1000000007); 
        }
        return dp[n]%(1000000007);
    }
}
// 动态规划方程

// f(n)=f(n-1)+f(n-2)+f(n-3)

面试题 17.16. 按摩师

在这里插入图片描述

class Solution {
    public int massage(int[] nums) {
        int len = nums.length;
        if(len==0){
            return 0;
        }

        if(len==1){
            return nums[0];
        }

        // dp[i][0]:区间[0,i]里接收预约请求,并且下标为i的这一天不接受预约
        // dp[i][1]:区间[0,i]里接收预约请求,并且下标为i的这一天接收预约

        int[][] dp = new int[len][2];
        dp[0][0] =0;
        dp[0][1] = nums[0];

        for(int i=1;i<len;i++){
            dp[i][0] = Math.max(dp[i-1][0],dp[i-1][1]);
            dp[i][1] = dp[i-1][0] + nums[i];
        }
        return Math.max(dp[len-1][0],dp[len-1][1]);
    }
}

翻转数位

在这里插入图片描述

class Solution {
    public int reverseBits(int num) {
        int[] current = new int[32];
        int[] reverse = new int[32];
        int max = 0;


        if((num&1)==1){
            current[0]=1;
            reverse[0]=1;
        }else{
            current[0]=0;
            reverse[0]=1;
        }
        num=num>>1;
        for(int i=1;i<32;i++){
            if((num&1)==1){
                current[i]=current[i-1]+1;
                reverse[i]=reverse[i-1]+1;
            }else{
                current[i]=0;
                reverse[i]=current[i-1]+1;
            }
            if(reverse[i]>max){
                max = reverse[i];
            }
            num=num>>1;
        }
        return max;
    }
}

// current[i] 表示包含第i位的从num二进制低位至第i位联系1的最长长度
// reverse[i] 表示包含第i位的从低位到第i位最多翻转1个0->1的连续1的最长长度
// 用num[i] 表示整数num第i位的值
// 遍历num所有位数,也就是32位,reverse数组中最大值就是答案
//  转态方程
//  current[i]= num[i]==1?current[i-1]+1:0;
//  reverse[i]= num[i]==1?reverse[i-1]+1:current[i-1]+1




除数博弈

在这里插入图片描述

在这里插入图片描述

class Solution {
    public boolean divisorGame(int n) {
        if(n==1){
            return false;
        }

        if(n==2){
            return true;
        }
        
        boolean[] flag = new boolean[n+1];

        flag[1] = false;
        flag[2] = true;

        for(int i=3;i<=n;i++){
            for(int j=1;j<i;j++){
                if((i%j==0)&&(!flag[i-j])){
                    flag[i] =true;
                    break;
                }
            }
        }
        return flag[n];
    }
}






// 爱丽丝先开局

判断子序列

1.双指针

class Solution {
    public boolean isSubsequence(String s, String t) {
        // s是子序列
        int m = s.length();
        // t是非子序列
        int n = t.length();
        int i = 0;
        int j = 0;

        while(i<m&&j<n){
            if(s.charAt(i)==t.charAt(j)){
                j++;
            }
            i++;
        }

        return j==m;

    }
}

2、动态规划方法
在这里插入图片描述
在这里插入图片描述

class Solution {
    public boolean isSubsequence(String s, String t) {
        int n = s.length(), m = t.length();

        int[][] f = new int[m + 1][26];
        for (int i = 0; i < 26; i++) {
            f[m][i] = m;
        }

        for (int i = m - 1; i >= 0; i--) {
            for (int j = 0; j < 26; j++) {
                if (t.charAt(i) == j + 'a')
                    f[i][j] = i;
                else
                    f[i][j] = f[i + 1][j];
            }
        }
        int add = 0;
        for (int i = 0; i < n; i++) {
            if (f[add][s.charAt(i) - 'a'] == m) {
                return false;
            }
            add = f[add][s.charAt(i) - 'a'] + 1;
        }
        return true;
    }
}

传递信息

在这里插入图片描述

class Solution {
    public int numWays(int n, int[][] relation, int k) {
        int[][] dp = new int[k + 1][n];
        dp[0][0] = 1;
        for (int i = 0; i < k; i++) {
            for (int[] edge : relation) {
                int src = edge[0], dst = edge[1];
                dp[i + 1][dst] += dp[i][src];
            }
        }
        return dp[k][n - 1];
    }
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值