买卖股票的最佳时机(I - III) 动态规划入门

股票问题(动态规划入门)


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

题意:给定股票每一天的价格 prices,只允许买入一次卖出一次,求能获得的最大收益。

思路

状态设计:

f[i][0] 表示第 i 天持有股票的最大收益
f[i][1] 表示第 i 天不持有股票的最大收益
(注意第 i 天的股票价格为 prices[i-1])

状态转移:

  • f[i][0] 可以由 f[i-1][0] 或者 -prices[i-1] 转移而来(第 i 天持有股票,要么前一天就持有股票,即 f[i-1][0],要么在这一天之前,什么也没有操作,即收益为 0,那么第 i 天购入股票,花费 prices[i-1],总收益为 -prices[i-1])。
  • f[i][1] 可以由 f[i-1][1] 或者 f[i-1][0] + prices[i-1] 转移而来(第 i 天不持有股票,要么前一天就不持有股票,即 f[i-1][1],要么在这一天之前持有,第 i 天卖出股票,收益 prices[i-1],总收益为 f[i-1][0] + prices[i-1])。

即有转移方程(最大收益,从两个转移中取 max):

f[i][0] = max(f[i-1][0], -prices[i-1]);
f[i][1] = max(f[i-1][1], f[i-1][0] + prices[i-1]);

初始值设定:

  • f[0][0]:表示“还没开始操作时,手里有股票”的最大收益。实际上这种情况在现实中并不存在,但为了让后续转移方程统一,我们假设如果在第1天(下标0)买入股票,收益就是 -prices[0]。这样做的本质是让 f[0][0] 对应于 prices[-1],即还未开始操作的状态。
  • f[0][1]:表示“还没开始操作时,手里没有股票”的最大收益,这种情况下收益自然是0。

这样设置的好处是,后续每一天的状态转移都可以直接用统一的公式,不需要对第一天做特殊处理。

代码实现:

int f[100010][2];
class Solution {
public:
    int maxProfit(vector<int>& prices) {
        int n = prices.size();
        // 还可用滚动数组优化
        f[0][0] = -prices[0];
        for (int i = 1; i <= n; i++) {
            f[i][0] = max(f[i-1][0], -prices[i-1]);
            f[i][1] = max(f[i-1][1], f[i-1][0] + prices[i-1]);
        }
        return f[n][1];
    }
};

题外话:

这题当然还有更优解,维护当日之前的最小值,用当日的值减去该最小值,以此更新答案即可。

class Solution {
public:
    int maxProfit(vector<int>& prices) {
        int n = prices.size();
        int mn = prices[0], res = 0;
        for (int i = 0; i < n; i++) {
            mn = min(mn, prices[i]);
            res = max(res, prices[i] - mn);
        }
        return res;
    }
};

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

题意:给定股票每一天的价格 prices,允许买入卖出多次,但是任意时刻最多只能持有一支股票,即买入一支股票后,卖出其才能再次购买股票,求能获得的最大收益。

思路

这题与买卖股票的最佳时机 I 的最大区别就是这里可以进行多次买入卖出操作。在状态设计上不变,状态转移上有所变化。

状态设计(与 I 相同):

f[i][0] 表示第 i 天持有股票的最大收益
f[i][1] 表示第 i 天不持有股票的最大收益
(注意第 i 天的股票价格为 prices[i-1])

状态转移:

  • f[i][0] 可以由 f[i-1][0] 或者 f[i-1][1] - prices[i-1] 转移而来(第 i 天持有股票,要么前一天就持有股票,即 f[i-1][0],要么在这一天之前不持有股票,即 f[i-1][1],那么第 i 天购入股票,花费 prices[i-1],总收益为 f[i-1][1] - prices[i-1])。
  • f[i][1] 可以由 f[i-1][1] 或者 f[i-1][0] + prices[i-1] 转移而来(第 i 天不持有股票,要么前一天就不持有股票,即 f[i-1][1],要么在这一天之前持有,第 i 天卖出股票,收益 prices[i-1],总收益为 f[i-1][0] + prices[i-1])。

即有转移方程(最大收益,从两个转移中取 max):

f[i][0] = max(f[i-1][0], f[i-1][1] - prices[i-1]);
f[i][1] = max(f[i-1][1], f[i-1][0] + prices[i-1]);

初始值设定与 I 相同,不再赘述

代码实现:

int f[100010][2];
class Solution {
public:
    int maxProfit(vector<int>& prices) {
        int n = prices.size();
        f[0][0] = -prices[0];
        f[0][1] = 0;
        for (int i = 1; i < n; i++) {
            f[i][0] = max(f[i-1][0], f[i-1][1] - prices[i]);
            f[i][1] = max(f[i-1][1], f[i-1][0] + prices[i]);
        }
        return f[n-1][1];
    }
};

题外话:

本题也可以用贪心法直接求解:只要今天的价格比昨天高,就把差价加到收益里。

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

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

题意:给定股票每一天的价格 prices,允许买入卖出最多两次,但是任意时刻最多只能持有一支股票,即买入一支股票后,卖出其才能再次购买股票,求能获得的最大收益。

思路

状态设计:

f[i][0] 表示第 i 天第一次买入股票后持有的最大收益
f[i][1] 表示第 i 天第一次卖出股票后不持有的最大收益
f[i][2] 表示第 i 天第二次买入股票后持有的最大收益
f[i][3] 表示第 i 天第二次卖出股票后不持有的最大收益
(注意第 i 天的股票价格为 prices[i-1])

状态转移:

  • f[i][0] 可以由 f[i-1][0]-prices[i-1] 转移而来(第一次买入,或者之前就持有)。
  • f[i][1] 可以由 f[i-1][1]f[i-1][0] + prices[i-1] 转移而来(第一次卖出,或者之前就已卖出)。
  • f[i][2] 可以由 f[i-1][2]f[i-1][1] - prices[i-1] 转移而来(第二次买入,或者之前就持有)。
  • f[i][3] 可以由 f[i-1][3]f[i-1][2] + prices[i-1] 转移而来(第二次卖出,或者之前就已卖出)。

即有转移方程:

f[i][0] = max(f[i-1][0], -prices[i-1]);
f[i][1] = max(f[i-1][1], f[i-1][0] + prices[i-1]);
f[i][2] = max(f[i-1][2], f[i-1][1] - prices[i-1]);
f[i][3] = max(f[i-1][3], f[i-1][2] + prices[i-1]);

初始值设定:

  • f[0][0] = -prices[0],表示第1天买入第一次股票后的收益。
  • f[0][1] = 0,表示第1天卖出第一次股票后的收益。
  • f[0][2] = -prices[0],表示第1天买入第二次股票后的收益(只能在第一次卖出后买入,但初始化时可设为同上,后续转移会自动修正)。
  • f[0][3] = 0,表示第1天卖出第二次股票后的收益。

代码实现:

int f[100010][4];
class Solution {
public:
    int maxProfit(vector<int>& prices) {
        int n = prices.size();
        f[0][0] = f[0][2] = -prices[0];
        f[0][1] = f[0][3] = 0;
        for (int i = 1; i < n; i++) {
            f[i][0] = max(f[i-1][0], -prices[i]);
            f[i][1] = max(f[i-1][1], f[i-1][0] + prices[i]);
            f[i][2] = max(f[i-1][2], f[i-1][1] - prices[i]);
            f[i][3] = max(f[i-1][3], f[i-1][2] + prices[i]);
        }
        return f[n-1][3];
    }
};
posted on 2025-07-17 16:15  __NK  阅读(28)  评论(0)    收藏  举报