算法之DP——买卖股票

一直对动态规划的题有种恐惧,每当做这类题的时候,总是感觉无从下手,思考一会就放弃了,然后去评论区贯彻我的拿来主义,到头来还是没真正搞懂。没办法,战胜恐惧的最好办法就是面对恐惧。目前只能用最浅显的理解去解答这类题目,包括之前的秋叶收藏集,又何尝不是呢?

1. 买卖股票的最佳时机 I

给定一个数组,它的第 i 个元素是一支给定股票第 i 天的价格。如果你最多只允许完成一笔交易(即买入和卖出一支股票一次),设计一个算法来计算你所能获取的最大利润。注意:你不能在买入股票前卖出股票。

思路

首先,最直观的理解:当遍历整个数组的时候,每到一个位置,就去判断一下当前股票如果卖出的话的利润,然后更新最大值。而为了保持利润的最大值,应该记录到当前i位置的最小值(不包括当前 i ),以此作为买入。
代码如下:

public int maxProfit(int[] prices) {
        if (prices.length <= 1) {
            return 0;
        }
        int min = prices[0]; //初始化为数组首元素
        int max = 0;
        for (int i = 1; i < prices.length; i++) {
            max = Math.max(max, prices[i] - min); //如果当前卖出
            min = Math.min(min, prices[i]);       //更新最小值
        }
        return max;
    }

2.买卖股票的最佳时机 II

难度简单

给定一个数组,它的第 i 个元素是一支给定股票第 i 天的价格。
设计一个算法来计算你所能获取的最大利润。你可以尽可能地完成更多的交易(多次买卖一支股票)。
注意:你不能同时参与多笔交易(你必须在再次购买前出售掉之前的股票)。

思路

采用动态规划,每当到一个 i 位置的时候无非就两种状态——持有股票和不持有股票,所以每一个状态都可以由上一状态决定
代码如下:

public int maxProfit(int[] prices) {
        if (prices.length <= 1) {
            return 0;
        }
        int len = prices.length;
        int[][] dp = new int[len][2]; //dp[i][0] 表示第i天不持有,dp[i][1]表示第i天持有
        dp[0][0] = 0;
        dp[0][1] = -prices[0];
        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], dp[i - 1][0] - prices[i]);
        }
        return dp[len - 1][0];
    }

3.买卖股票的最佳时机 III

给定一个数组,它的第 i 个元素是一支给定的股票在第 i 天的价格。
设计一个算法来计算你所能获取的最大利润。你最多可以完成 两笔 交易。
注意: 你不能同时参与多笔交易(你必须在再次购买前出售掉之前的股票)。

思路

由于限制了交易次数,故不能向上一题一样无限次交易,此时需要记录交易次数这个状态。
代码如下:

 public int maxProfit(int[] prices) {
        if (prices.length <= 1) {
            return 0;
        }
        int k = 2;
        int[][][] dp = new int[prices.length][k + 1][2];
        for (int i = 0; i < prices.length; i++) {
            for (int j = k; j > 0; j--) {
                if (i == 0) {
                    //第i天,还有j次,手里没有股票,当i=0,手里没股票,最大利润为0
                    dp[0][j][0] = 0;
                    dp[0][j][1] = -prices[0];
                    continue;
                }
                //今天手里没股票,比较MAX(前一天可能没股票,前一天有股票但是今天卖出去了,卖出去就有利润,所以+ prices[i])
                dp[i][j][0] = Math.max(dp[i - 1][j][0], dp[i - 1][j][1] + prices[i]);
                //今天手里有股票,比较MAX(前一天可能有股票,前一天没股票但是今天买了,买了就有成本,所以- prices[i])
                dp[i][j][1] = Math.max(dp[i - 1][j][1], dp[i - 1][j - 1][0] - prices[i]);
            }
        }
        return dp[prices.length - 1][k][0];
    }

4. 买卖股票的最佳时机 IV

给定一个数组,它的第 i 个元素是一支给定的股票在第 i 天的价格。
设计一个算法来计算你所能获取的最大利润。你最多可以完成 k 笔交易。
注意: 你不能同时参与多笔交易(你必须在再次购买前出售掉之前的股票)。

思路

此题同上,只不过需要判断一下k的大小,进而转换为无限次交易或者交易指定次数

 public int maxProfit(int k, int[] prices) {
        if (prices.length <= 1) {
            return 0;
        }
        int n = prices.length;
        //当k非常大时转为无限次交易
        if(k>=n/2) {
            int dp0=0,dp1=-prices[0];
            for(int i=1;i<n;++i) {
                int tmp = dp0;
                dp0 = Math.max(dp0,dp1+prices[i]);
                dp1 = Math.max(dp1,dp0-prices[i]);
            }
            return Math.max(dp0,dp1);
        }
        int[][][] dp = new int[prices.length][k + 1][2];
        for (int i = 0; i < prices.length; i++) {
            for (int j = k; j > 0; j--) {
                if (i == 0) {
                    //第i天,还有j次,手里没有股票,当i=0,手里没股票,最大利润为0
                    dp[0][j][0] = 0;
                    dp[0][j][1] = -prices[0];
                    continue;
                }
                //今天手里没股票,比较MAX(前一天可能没股票,前一天有股票但是今天卖出去了,卖出去就有利润,所以+ prices[i])
                dp[i][j][0] = Math.max(dp[i - 1][j][0], dp[i - 1][j][1] + prices[i]);
                //今天手里有股票,比较MAX(前一天可能有股票,前一天没股票但是今天买了,买了就有成本,所以- prices[i])
                dp[i][j][1] = Math.max(dp[i - 1][j][1], dp[i - 1][j - 1][0] - prices[i]);
            }
        }
        return dp[prices.length - 1][k][0];
    }
本作品采用《CC 协议》,转载必须注明作者和本文链接
讨论数量: 1
jiangjun

能挣钱吗

3年前 评论
VCBAL 3年前

讨论应以学习和精进为目的。请勿发布不友善或者负能量的内容,与人为善,比聪明更重要!