算法学习 —— 动态规划练习(一)

一、买卖股票的最佳时机(LeetCode-121)

1.1 题目介绍

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。

1.2 解题思路

想要利润最大,尽可能地要保证购买的时候价格最低,总利润最高。由此可以列出状态转移方程。

buy = min{buy,price}
profilt = max{profilt,price-buy}

1.3 解法

    public int maxProfit(int[] prices) {
        int profilt = 0;
        int buy = Integer.MAX_VALUE;
        for (int price : prices) {
            buy = Math.min(buy, price);
            profilt = Math.max(profilt, price - buy);
        }
        return profilt;
    }

二、使用最小花费爬楼梯(LeetCode-746)

2.1 题目介绍

数组的每个索引做为一个阶梯,第 i个阶梯对应着一个非负数的体力花费值 costi

每当你爬上一个阶梯你都要花费对应的体力花费值,然后你可以选择继续爬一个阶梯或者爬两个阶梯。

您需要找到达到楼层顶部的最低花费。在开始时,你可以选择从索引为 0 或 1 的元素作为初始阶梯。

示例 1:
输入: cost = [10, 15, 20]
输出: 15
解释: 最低花费是从cost[1]开始,然后走两步即可到阶梯顶,一共花费15。

示例 2:
输入: cost = [1, 100, 1, 1, 1, 100, 1, 1, 100, 1]
输出: 6
解释: 最低花费方式是从cost[0]开始,逐个经过那些1,跳过cost[3],一共花费6。
注意:
cost 的长度将会在 [2, 1000]。
每一个 cost[i] 将会是一个Integer类型,范围为 [0, 999]。

2.2 解题思路

以样本为例cost = [10, 15, 20]
n = 3;

最后一步:
根据题意我们知道,最后一步不一定是cost[2](即20),还有可能是之后的值,我们这里假设有cost[3]且cost[3] = 0,这里我们就统一认为最后一步是cost[3],因为它的需要用的体力值设置为0。所以最后走到cost[2]所用的总体力值,和走到cost[3]的是一样的。

子问题
最后一步消耗的体力值是cost[3],总共消耗的体力值为f[3]。
走到了最后一步之后,往前回退,所面对的选择是,要么是倒退一步,要么是倒退两步。

于是有了子问题方程:

f[3] = min(f[1],f[2])+cost[3]

状态转移方程

f[x] = min(f[x-1],f[x-2])+cost[x]

初始条件
第一步可以从cost[0]开始走,也可以从cost[1]开始走。于是有了
f[0] = cost[0]
f[1] = cost[1]

边界
int costLength = cost.length;
cost[costLengh+1] = 0
f[costLengh+1]为最终的解。

2.3 解法


public int minCostClimbingStairs(int[] cost) {

        int n = cost.length;
        int[] f = new int[n+1];
        //初始化
        f[0] = cost[0];
        f[1] = cost[1];
        for (int i = 2; i <= n; i++) {
            int tempCost = 0;
            if (i == n) {
            	//cost[n] = 0
                tempCost = 0;
            } else {
                tempCost = cost[i];
            }
            f[i] = Math.min(f[i - 1], f[i - 2]) + tempCost;
        }
        return f[n];
    }

三、爬楼梯(LeetCode-70)

3.1 题目介绍

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 阶

3.2 解题思路

最后一步:

最后一步是第n个台阶,我们这边假设为4。

子问题
最后一步到第4个台阶,假设总共有走法f[4]。
如果是走到第3个台阶的话,就只能再走1步
如果走到第2个台阶的话,可以走2步,或者走2次,分别走1步。

于是有了子问题方程:

f[4] = f[4-step1] + f[4-step2]
f[4] = f[3] + f[2]

状态转移方程

f[x] = f[x-1] + f[x-2]

3.3 解法


class Solution {
    public int climbStairs(int n) {
        int[] f = new int[n+1];
        if(n<=2){
            return n;
        }
        //初始化
        f[0] = 0;
        f[1] = 1;
        f[2] = 2;
        //f[x] = f[x-1] + f[x-2]
        for(int i=3;i<=n;i++){
            f[i] = f[i-1]+f[i-2];
        }
        return f[n];
        
    }
}

四、最大子序和(LeetCode-53)

4.1 题目介绍

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

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

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

4.2 解题思路

分析能不能使用DP,要考虑符不符合DP的子问题重叠,即一个子问题,依赖于上一个子问题

简化输入,只有-2,则dp[0] 为最大子序列和,dp[0] = -2,

假设只有,-2,1,则dp[1]会参考dp[0],会比较1与-2+1的大小,从而确定要不要前面的-2,由此可得出

$dp[i] = \max\{ dp[i-1]+nums[i],nums[i]\} $

4.3 解法


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

参考文档

LeetCode中动态规划题解合集

posted @ 2019-05-13 07:27  清泉白石  阅读(428)  评论(0编辑  收藏  举报