力扣刷题笔记:股票问题(一)

121.买卖股票的最佳时机

应当这样理解本题:

  1. 只要某天的股票价格高于买入的价格,那么就可以考虑卖出股票,如果以后有更好的方案则改变方案;
  2. 假如我打算在某天卖出股票,那么一定当股票是在其价格最低点买入的时候,利润才是最大的;
  3. 如果在今天之前的价格都高于当前价格,则考虑今天才买入,如果未来卖出,一定比之前任意一天买入的利润要高。
    综上,任意一天需要考虑的应当是两个值:之前的最低价格和当前价格,当当前价格高于最低价格时考虑卖出,否则更新最低价格。
    第一天的时候只能买入,不可卖出。
class Solution {
    public int maxProfit(int[] prices) {
        int profit = 0; // 最大利润
        int minValue = prices[0];
        for (int i : prices) {
            if (i <= minValue) {    //第一天的价格等于最低价,修改最低价格,所以下标可以从0开始
                minValue = i;
            } else {
                profit = Math.max(profit, i - minValue);
            }
        }
        return profit;
    }
}

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

714. 买卖股票的最佳时机含手续费

这两个题完全可以合并在一起,因为思路是一样的,122题只是714题中手续费为0的特殊情况。

1.贪心算法

此处贪心算法的本质是允许后悔,即只要当前的价格可以产生利润,那就卖出,如果之后发现有更好的利润的时候就改变卖出的时机,换取更多的利润。重点在于,由于每次卖出之后允许后悔,即相当于每次卖出之后可以立即以当天的股票价格再买入股票(无需手续费),如果某天的股价加上手续费还低于手中股票的购入的费用,则可以重新购入股票,注意当天的股价应当包括手续费;如果某天的股价(包括手续费)等于买入价格,则不作处理,因为可能意味着多一次的手续费。

对于“由于每次卖出之后允许后悔,即相当于每次卖出之后可以立即以当天的股票价格再买入股票(无需手续费)” ,可以这样理解 ,假如手续费是fee,买入时股价为a,则买入价格是a+fee,假设未来股价b>a+fee,c>b,则显然a+fee的价格买入,c的价格时卖出利润最多,观察如下表达式:

c - (a+fee) = (c - b) + (b - (a+fee))

等式右边第一项即表示用b的价格(不含手续费)再次买入的收益。

//122. 不含手续费
class Solution {
    public int maxProfit(int[] prices) {
        int ans = 0;
        int buy = prices[0];        // 假设第一天买入
        for (int i = 1; i < prices.length; i++) {
            if (prices[i] < buy) {  // 当前的价格更低,如果换成今天买入,未来的收益更高(后悔,重新买入)
                buy = prices[i];
            } else if (prices[i] > buy) {   // 当前的价格卖出有利润,立刻卖出,未来可以后悔
                ans += prices[i] - buy;
                buy = prices[i];
            }
        }
        return ans;
    }
}
// 上述程序可以简化
class Solution {
    public int maxProfit(int[] prices) {
        int ans = 0;
        for (int i = 1; i < prices.length; i++) {
            ans += Math.max(0, prices[i] - prices[i - 1]);
        }
        return ans;
    }
}

//714. 考虑手续费
class Solution {
    public int maxProfit(int[] prices, int fee) {
        int ans = 0;
        int buy = prices[0] + fee;        // 假设第一天买入
        for (int i = 1; i < prices.length; i++) {
            if (prices[i] + fee < buy) {  // 当前的价格更低,如果换成今天买入,未来的收益更高(后悔,重新买入)
                buy = prices[i];
            } else if (prices[i] > buy) {   // 当前的价格卖出有利润,立刻卖出,未来可以后悔
                ans += prices[i] - buy;
                buy = prices[i];
            }
        }
        return ans;
    }
}

2. 动态规划

也可以使用动态规划的方法求解,即一共做n次决策,每次决策是买入还是卖出或者是等待(不买入或不卖出),每次决策的结果对应一种状态。每种状态分别包含手中有股票和没有股票时的最大利润。初始状态,只能考虑买入股票(手中有股票)或者不买入(手中没有股票),对应的利润值分别是-price[0]0;以后的每一天状态如下 :

  • 手中有股票 :前一天手中没有股票并在今天买入(如果有手续费则支付手续费);前一天手中有股票并且今天没有卖出。
  • 手中没有股票:前一天手中有股票并在今天卖出;前一天手中没有股票并且今天没有买入。
    如果用一个n*2的数组dp表示状态表,dp[i][0]表示手中没有股票时的利润,dp[i][1]表示手中有股票时的利润,则状态转移过程如下 :
dp[i][0]=max\{dp[i−1][0],dp[i−1][1]+prices[i]−fee \}
dp[i][1]=max\{dp[i−1][1],dp[i−1][0]−prices[i] \}

当然也可以在股票买入的时候支付手续费,对应初始状态和状态转移方程如下:

dp[0][0] = 0;
dp[0][1] = -prices[0] - fee;
dp[i][0]=max\{dp[i−1][0],dp[i−1][1]+prices[i] \}
dp[i][1]=max\{dp[i−1][1],dp[i−1][0]−prices[i]−fee \}

为了节省空间,考虑到每天的状态只与前一天的状态有关,所以只动态地维护前一天的状态即可。

// 未考虑手续费的程序
class Solution {
    public int maxProfit(int[] prices) {
        int n = prices.length;
        int dp0 = 0, dp1 = -prices[0];
        for (int i = 1; i < n; ++i) {
            int newDp0 = Math.max(dp0, dp1 + prices[i]);
            int newDp1 = Math.max(dp1, dp0 - prices[i]);
            dp0 = newDp0;
            dp1 = newDp1;
        }
        return dp0;
    }
}

309. 最佳买股票时机含冷冻期

这里依然采用动态规划的方法求解,与前一题不同的是,第i的利润情况分为三种状态:
dp[i][0]: 手上持有股票的最大利润;

  • 手中的股票是前一天剩下的且今天没有买入;
  • 手中的股票是今天买入的,此时的前提是昨天没有卖出(不可从dp[i-1][1]转来);
    dp[i][1]: 手上不持有股票,今天卖出了股票(i+1天是冷冻期)的最大利润;
  • 需要前一天手中有股票,即从dp[i-1][0]状态转来
    dp[i][2]: 手上不持有股票,之前某天卖出股票(i+1天非冷冻期)的最大利润;
  • 前一天手中没有股票且今天也没有买入。
    状态转移方程如下:
f[i][0]=max(f[i−1][0],f[i−1][2]−prices[i])
f[i][1]=f[i−1][0]+prices[i]
f[i][2]=max(f[i−1][1],f[i−1][2])
class Solution {
    public int maxProfit(int[] prices) {
        if (prices.length == 0) {
            return 0;
        }
        int n = prices.length;
        int[][] dp = new int[n][3];
        f[0][0] = -prices[0];
        for (int i = 1; i < n; ++i) {
            dp[i][0] = Math.max(dp[i - 1][0], dp[i - 1][2] - prices[i]);
            dp[i][1] = dp[i - 1][0] + prices[i];
            dp[i][2] = Math.max(dp[i - 1][1], dp[i - 1][2]);
        }
        return Math.max(dp[n - 1][1], dp[n - 1][2]);
    }
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值