【leetcode】动态规划

参考: Krahets

斐波那契数列

在这里插入图片描述

int fibonacci(int n) {
    if (n == 0) return 0;          // 若求 f(0) 则直接返回 0
    int[] dp = new int[n + 1];     // 状态定义:一维dp:第i个数的斐波那契数
    dp[1] = 1;                     // 初始化 f(0), f(1)
    for (int i = 2; i <= n; i++) { // 循环条件:遍历 2 ~ n
        dp[i] = dp[i - 1] + dp[i - 2]; // 状态转移方程
    }
    return dp[n];                  // 返回 f(n)
}

最优蛋糕售价

// 输入蛋糕价格列表 priceList ,求重量为 n 蛋糕的最高售价
int maxCakePrice(int n, int[] priceList) {
    if (n <= 1) return priceList[n];  // 蛋糕重量 <= 1 时直接返回
    int[] dp = new int[n + 1];        // 状态定义:一维dp:重量为i的蛋糕切割后的最大售价 + 初始化
    for (int j = 1; j <= n; j++) {    // 循环条件:遍历 1 ~ n
        for (int i = 0; i < j; i++)   // 选择规则:从 j 种组合中选择最高售价的组合作为 f(j)
            dp[j] = Math.max(dp[j], dp[i] + priceList[j - i]); //状态转移方程
    }
    return dp[n];
}

爬楼梯

在这里插入图片描述

    public int climbStairs(int n) {
        int[] dp = new int[n + 1]; //状态定义:一维dp,第i层有dp[i]种爬楼梯方案
        dp[0] = 1; // 初始化
        dp[1] = 1;
        for(int j = 2; j <= n; j++){ //循环:2~n
            dp[j] = dp[j-1] + dp[j-2]; // 方程:从j-1爬上来的方案 + 从j-2爬上来的方案
        }
        return dp[n];
    }

最大子数组和

在这里插入图片描述

    public int maxSubArray(int[] nums) {
        int[] dp = new int[nums.length]; // 1-D 代表以nums[i]结尾的最大子序列和
        dp[0] = nums[0]; // 初始化
        int rs = nums[0]; //结果返回的是dp数组中的最大值
        for(int i = 1; i < nums.length; i++){ // 循环:1~num.length - 1
            if(dp[i-1] < 0) dp[i] = nums[i]; // 方程:如果dp[i-1]<0,对最大和只有负贡献,只添加nums[i]
            else dp[i] = dp[i-1] + nums[i]; 
            rs = Math.max(rs, dp[i]);
        }
        return rs;
    }

珠宝的最高价值

在这里插入图片描述

    public int jewelleryValue(int[][] frame) {
        int m = frame.length, n = frame[0].length;
        int[][] dp = new int[m + 1][n + 1]; //2D 移动到i j 位置的最高价值
        for(int i = 1; i <= m; i++){ //循环:遍历2D数组
            for(int j = 1; j <= n; j++){
            //方程:从上面来和从左边来的最大值 + 自身值
                 dp[i][j] = Math.max(dp[i-1][j], dp[i][j-1]) + frame[i - 1][j - 1];
            }
        }
        return dp[m][n];
    }

小tip
这里开拓了新的一行一列来规避了首行/首列的特殊情况

解密数字

在这里插入图片描述

    public int crackNumber(int ciphertext) {
        String ciper = String.valueOf(ciphertext); //int转string
        int[] dp = new int[ciper.length() + 1]; // dp代表到了第i个数字时的破译方案
        dp[0] = 1; //初始化
        dp[1] = 1;
        for(int i = 2; i <= ciper.length(); i++){ //循环
        	/*
        		判断子串的范围: [10,25] 说明能翻译两次 其余只能翻译一次
        	*/
            String tmp  = ciper.substring(i-2, i); 
            if(tmp.compareTo("10") >= 0 && tmp.compareTo("26") < 0) dp[i] = dp[i-2] + dp[i - 1]; //方程
            else dp[i] = dp[i - 1];
        }
        return dp[ciper.length()] ;
    }

丑数

在这里插入图片描述

public int nthUglyNumber(int n) {
    int[] dp  = new int[n]; //记录丑数
    dp[0] = 1;
    int a = 0, b = 0, c = 0;
    for(int i = 1; i < n; i++){
    //丑数判断算法:丑数一定是由2 3 5当中某个数相乘得来,所以每次更新取最小丑数并让对应的下标++
        dp[i] = Math.min(dp[a]*2,Math.min(dp[b]*3,dp[c]*5));
        if(dp[i] == dp[a]*2) a++;
        if(dp[i] == dp[b]*3) b++;
        if(dp[i] == dp[c]*5) c++;
    }
    return dp[n-1];
}

统计结果概率

在这里插入图片描述

public double[] statisticsProbability(int num) {
    double[] dp = new double[6]; //一枚骰子掷出的所有点数总和的概率
    Arrays.fill(dp, 1.0/6.0);
    for(int i = 2; i <= num; i++){ //从第二枚骰子开始
        double[] tmp = new double[5*i + 1]; //第i枚骰子掷出的所有点数总和的概率
        for(int j = 0; j < dp.length; j++){ //正向递推,注意是遍历dp算下一个dp
            for(int k = 0; k < 6; k++){ 
                tmp[j+k] += dp[j] / 6.0; 
            }
        }
        dp = tmp; //更新dp
    }
    return dp;

最长公共子序列

在这里插入图片描述

public int longestCommonSubsequence(String text1, String text2) {
    int m = text1.length(), n = text2.length();
    int[][] dp = new int[m+1][n+1]; //dp[i][j]代表text1[1:i]和text2[1:j]的最长子序列;
    for(int i = 1; i <= m; i++){
        for(int j = 1; j <= n; j++){
            //如果最后一位相等 最长子序列++
            if(text1.charAt(i - 1) == text2.charAt(j - 1)) dp[i][j] = dp[i-1][j-1] + 1;
            else dp[i][j] = Math.max(dp[i-1][j],dp[i][j-1]);
        }
    }
    return dp[m][n];
}

变种题

指思想是动归的题

二叉树的最大路径和

在这里插入图片描述

int maxSum = Integer.MIN_VALUE;
public int maxPathSum(TreeNode root) {
    dfs(root);
    return maxSum;
}
public int dfs(TreeNode root){
    if(root == null) return 0;
    int left = dfs(root.left);
    int right = dfs(root.right);
    // 一个子树内部的最大路径和
    int innerMaxSum = root.val + left + right;
    maxSum = Math.max(innerMaxSum,maxSum);
    // 子树能向父节点“提供”的最大路径和
    int outMaxSum = root.val + Math.max(Math.max(left,right),0);
    return outMaxSum > 0 ? outMaxSum:0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值