Leetcode初级算法卡片中的动态规划

爬楼梯70

假设你正在爬楼梯。需要 n 阶你才能到达楼顶。

每次你可以爬 1 或 2 个台阶。你有多少种不同的方法可以爬到楼顶呢?

**注意:**给定 n 是一个正整数。

示例 1:

输入: 2
输出: 2
解释: 有两种方法可以爬到楼顶。
1.  1 阶 + 1 阶
2.  2 阶

示例 2:

输入: 3
输出: 3
解释: 有三种方法可以爬到楼顶。
1.  1 阶 + 1 阶 + 1 阶
2.  1 阶 + 2 阶
3.  2 阶 + 1 阶

解法:

class Solution {
    int[] dp;
    
    public int climbStairs(int n) {
        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];
        }
        return dp[n];
    }
}

买卖股票的最佳时机121

给定一个数组,它的第 i 个元素是一支给定股票第 i 天的价格。

如果你最多只允许完成一笔交易(即买入和卖出一支股票一次),设计一个算法来计算你所能获取的最大利润。

注意:你不能在买入股票前卖出股票。

示例 1:

输入: [7,1,5,3,6,4]
输出: 5
解释: 在第 2 天(股票价格 = 1)的时候买入,在第 5 天(股票价格 = 6)的时候卖出,最大利润 = 6-1 = 5 。
注意利润不能是 7-1 = 6, 因为卖出价格需要大于买入价格;同时,你不能在买入前卖出股票。

示例 2:

输入: [7,6,4,3,1]
输出: 0
解释: 在这种情况下, 没有交易完成, 所以最大利润为 0。

解法:

把每个值看成函数曲线上的点,这个函数求导后的积分就是两点之差。牛顿莱布尼兹公式:
∫ a b f ( x ) d x = F ( x ) ∣ a b = F ( b ) − F ( a ) ∫_a^bf(x)dx = F(x)|_a^b = F(b) - F(a) abf(x)dx=F(x)ab=F(b)F(a)
这样就把减法问题转化成了加法问题。而这个加法问题又转化成了最长连续和问题。参考:https://leetcode-cn.com/problems/best-time-to-buy-and-sell-stock/solution/121-mai-mai-gu-piao-de-zui-jia-shi-ji-dp-7-xing-ji/

class Solution {
    public int maxProfit(int[] prices) {
        if (prices.length <= 1) return 0;
        // 求导
        int[] diff = new int[prices.length - 1];
        for (int i = 0; i < diff.length; i++) {
            diff[i] = prices[i + 1] - prices[i];
        }
        
        // 转换为最大连续和的问题
        // dp[i] = max(0, dp[i - 1] + diff[i])
        int[] dp = new int[diff.length];
        dp[0] = Math.max(0, diff[0]);
        int profit = dp[0];
        for (int i = 1; i < dp.length; i++) {
            dp[i] = Math.max(0, dp[i - 1] + diff[i]);
            profit = Math.max(profit, dp[i]);
        }
        return profit;
    }
}

优化掉dp数值

class Solution {
    public int maxProfit(int[] prices) {
        if (prices.length <= 1) return 0;
        // 求导
        int[] diff = new int[prices.length - 1];
        for (int i = 0; i < diff.length; i++) {
            diff[i] = prices[i + 1] - prices[i];
        }
        
        // 转换为最大连续和的问题
        // 优化:1维数组->一个值
        int last = 0;
        int profit = last;
        for (int i = 0; i < diff.length; i++) {
            last = Math.max(0, last + diff[i]);
            profit = Math.max(profit, last);
        }
        return profit;
    }
}

优化掉diff数组

class Solution {
    public int maxProfit(int[] prices) {
        int last = 0;
        int profit = last;
        for (int i = 0; i < prices.length - 1; i++) {
            last = Math.max(0, last + prices[i + 1] - prices[i]);
            profit = Math.max(profit, last);
        }
        return profit;
    }
}

优化完之后只需要扫描一次,现在一旦比买入价低了,就把买入点调到现在,计算现在值和买入值的最大差。

最大子序和53

给定一个整数数组 nums ,找到一个具有最大和的连续子数组(子数组最少包含一个元素),返回其最大和。

示例:

输入: [-2,1,-3,4,-1,2,1,-5,4],
输出: 6
解释: 连续子数组 [4,-1,2,1] 的和最大,为 6。

进阶:

如果你已经实现复杂度为 O(n) 的解法,尝试使用更为精妙的分治法求解。

解法:

不同于上一题的是,上一题中导数的值全为0则不挣钱,就是0;而在这个题中[-1]的最大连续和是-1,所以上一题那种以0为最小值的方法不适用,需要稍微修改。

class Solution {
    public int maxSubArray(int[] nums) {
        if (nums.length == 0) return 0;
        int last = nums[0];
        int maxLength = last;
        for (int i = 1; i < nums.length; i++) {
            last = Math.max(nums[i], last + nums[i]);
            maxLength = Math.max(maxLength, last);
        }
        return maxLength;
    }
}

如果当前和左边加起来比当前更小,那么左边就不应该是最大子序和的组成。

打家劫舍198

你是一个专业的小偷,计划偷窃沿街的房屋。每间房内都藏有一定的现金,影响你偷窃的唯一制约因素就是相邻的房屋装有相互连通的防盗系统,如果两间相邻的房屋在同一晚上被小偷闯入,系统会自动报警

给定一个代表每个房屋存放金额的非负整数数组,计算你 不触动警报装置的情况下 ,一夜之内能够偷窃到的最高金额。

示例 1:

输入: [1,2,3,1]
输出: 4
解释: 偷窃 1 号房屋 (金额 = 1) ,然后偷窃 3 号房屋 (金额 = 3)。
     偷窃到的最高金额 = 1 + 3 = 4 。

示例 2:

输入: [2,7,9,3,1]
输出: 12
解释: 偷窃 1 号房屋 (金额 = 2), 偷窃 3 号房屋 (金额 = 9),接着偷窃 5 号房屋 (金额 = 1)。
     偷窃到的最高金额 = 2 + 9 + 1 = 12 。

解法:

数组长度为0,输出0;长度为1,输出第一个元素;长度为2;输出两个元素中较大的一个。

直接用参数中的nums数组作dp数组。递推式:dp[i] = nums[i] + max(dp[i - 2], dp[i - 3])

dp[i]代表偷到第 i 家可以偷到的最大值。例子:[2, 1, 1, 2]的dp数组[2, 1, 3, 4]

class Solution {
    public int rob(int[] nums) {
        int len = nums.length;
        if (len == 0) return 0;
        if (len == 1) return nums[0];
        if (len == 2) return Math.max(nums[0], nums[1]);
        if (len == 3) return Math.max(nums[0] + nums[2], nums[1]);
        nums[2] = Math.max(nums[0] + nums[2], nums[1]);
        int maxn = nums[2];
        for (int i = 3; i < len; i++) {
            nums[i] = nums[i] + Math.max(nums[i - 2], nums[i - 3]);
            maxn = Math.max(maxn, nums[i]);
        }
        return maxn;
    }
}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值