股票买卖问题

股票买卖问题

主要分成以下四个逐步进阶的版本

  • 最多进行一次交易
  • 可以进行无数次交易
  • 最多进行两次交易
  • 最多进行 k k k次交易

最多进行一次交易

LeetCode121. 买卖股票的最佳时机

    给定一个数组 p r i c e s prices prices,它的第 i i i个元素 p r i c e s [ i ] prices[i] prices[i]表示一支给定股票第 i i i天的价格。
    你只能选择某一天买入这只股票,并选择在未来的某一个不同的日子卖出该股票。设计一个算法来计算你所能获取的最大利润。
    返回你可以从这笔交易中获取的最大利润。如果你不能获取任何利润,返回 0 0 0

思路

    使用动态规划: f [ i ] f[i] f[i]:表示前 i i i天所能收获的最大收益;分两种情况,一是第 i i i天卖出,那么就要指导前 i − 1 i-1 i1天的最小价格,这需要一个变量保存;二是第 i i i天不交易,那么 f [ i ] = f [ i − 1 ] f[i]=f[i-1] f[i]=f[i1]。综上, f [ i ] = m a x ( f [ i − 1 ] , p r i c e s [ i ] − m i n P ) f[i]=max(f[i-1],prices[i]-minP) f[i]=max(f[i1],prices[i]minP)

代码

class Solution {
    public int maxProfit(int[] prices) {
        int n = prices.length;
        int[] f = new int[n+1];
        for(int i = 1, minP = Integer.MAX_VALUE / 2; i <= n; i++) {
            f[i] = Math.max(f[i-1], prices[i-1] - minP);
            minP = Math.min(minP, prices[i-1]);
        }
        return f[n];
    }
}

可以进行无数次交易

LeetCode122. 买卖股票的最佳时机Ⅱ

    给定一个数组 p r i c e s prices prices,其中 p r i c e s [ i ] prices[i] prices[i]表示股票第 i i i天的价格。
    在每一天,你可能会决定购买和/或出售股票。你在任何时候最多只能持有一股股票。你也可以购买它,然后在同一天出售。
    返回你能获得的最大利润 。

思路

    贪心:每当后一天比当前天的股票价格高,那么就在这两天进行一次买卖交易。

代码

class Solution {
    public int maxProfit(int[] prices) {
        int res = 0;
        for(int i = 0; i + 1 < prices.length; i++) {
            if(prices[i+1] > prices[i]) {
                res += prices[i+1] - prices[i];
            }
        }
        return res;
    }
}

最多进行两次交易

LeetCode123. 买卖股票的最佳实际Ⅲ

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

思路

    前后缀分解:枚举第二次交易买入时机 i i i,那么此时所能获取的最大收益为 f [ i − 1 ] + m a x P − p r i c e s [ i ] f[i-1]+maxP-prices[i] f[i1]+maxPprices[i],其中 f [ i − 1 ] f[i-1] f[i1]就是最多买卖一次的前 i − 1 i-1 i1天的最大收益, m a x P maxP maxP是第 i i i天后的最大股票价格,这两者都可以提前预处理出来。

代码

class Solution {
    public int maxProfit(int[] prices) {
        int n = prices.length;
        int[] f = new int[n+1];
        for(int i = 1, minP = Integer.MAX_VALUE; i <= n; i++) {
            f[i] = Math.max(f[i-1], prices[i-1] - minP);
            minP = Math.min(minP, prices[i-1]);
        }
        int res = 0;
        for(int i = n, maxP = 0; i >= 1; i--) {
            res = Math.max(res, f[i-1] + maxP - prices[i-1]);
            maxP = Math.max(maxP, prices[i-1]);
        }
        return res;
    }
}

最多进行k次交易

LeetCode188. 买卖股票的最佳时机Ⅳ

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

思路

    状态机型DP: f [ i ] [ j ] f[i][j] f[i][j]表示此时手中没有股票,且花了 i i i天,已完成 j j j笔交易; g [ i ] [ j ] g[i][j] g[i][j]表示此时手中有股票,且花了 i i i天,正在进行第 j j j笔交易
状态机模型状态转移示意图
    根据上图易知: f [ i ] [ j ] = m a x ( f [ i − 1 ] [ j ] , g [ i − 1 ] [ j ] + w [ i ] ) f[i][j]=max(f[i-1][j],g[i-1][j]+w[i]) f[i][j]=max(f[i1][j],g[i1][j]+w[i]); g [ i ] [ j ] = m a x ( g [ i − 1 ] [ j ] , f [ i − 1 ] [ j − 1 ] − w [ i ] ) g[i][j]=max(g[i-1][j],f[i-1][j-1]-w[i]) g[i][j]=max(g[i1][j],f[i1][j1]w[i])
    另外,当 k ≥ n 2 k\ge\frac{n}{2} k2n,此题可以看作是可以进行无数次交易的特殊情况

代码

class Solution {
    public int maxProfit(int k, int[] prices) {
        int n = prices.length;
        if(k >= n / 2) {
            int res = 0;
            for(int i = 0; i + 1 < n; i++) {
                res += Math.max(0, prices[i+1] - prices[i]);
            }
            return res;
        }

        int INF = (int)(1e8);
        int[][] f = new int[n+1][k+1];
        int[][] g = new int[n+1][k+1];
        for(int i = 0; i <= n; i++) {
            Arrays.fill(f[i], -INF);
            Arrays.fill(g[i], -INF);
        }
        f[0][0] = 0;
        int res = 0;
        for(int i = 1; i <= n; i++) {
            for(int j = 0; j <= k; j++) {
                f[i][j] = Math.max(f[i-1][j], g[i-1][j] + prices[i-1]);
                if(j >= 1) {
                    g[i][j] = Math.max(g[i-1][j], f[i-1][j-1] - prices[i-1]);
                }
                res = Math.max(res, f[i][j]);
            }
        }
        return res;
    }
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值