阅读视图

发现新文章,点击刷新页面。

[Python3/Java/C++/Go/TypeScript] 一题一解:动态规划(清晰题解)

方法一:动态规划

我们定义 $f[i][j][k]$ 表示在前 $i$ 天内,最多进行 $j$ 笔交易,且当前状态为 $k$ 时的最大利润。这里的状态 $k$ 有三种可能:

  • 若 $k = 0$,表示当前没有持有股票。
  • 若 $k = 1$,表示当前持有一支股票。
  • 若 $k = 2$,表示当前持有一支股票的空头。

初始时,对任意 $j \in [1, k]$,都有 $f[0][j][1] = -prices[0]$ 和 $f[0][j][2] = prices[0]$。这表示在第 0 天买入一支股票或卖出一支股票的空头。

接下来,我们可以通过状态转移来更新 $f[i][j][k]$ 的值。对于每一天 $i$ 和每笔交易 $j$,我们可以根据当前状态 $k$ 来决定如何更新:

  • 若 $k = 0$,表示当前没有持有股票,这个状态可以由以下三种情况转移而来:
    • 前一天没有持有股票。
    • 前一天持有一支股票,并在今天卖出。
    • 前一天持有一支股票的空头,并在今天买回。
  • 若 $k = 1$,表示当前持有一支股票,这个状态可以由以下两种情况转移而来:
    • 前一天持有一支股票。
    • 前一天没有持有股票,并在今天买入。
  • 若 $k = 2$,表示当前持有一支股票的空头,这个状态可以由以下两种情况转移而来:
    • 前一天持有一支股票的空头。
    • 前一天没有持有股票,并在今天卖出。

即,对于 $1 \leq i < n$ 和 $1 \leq j \leq k$,我们有以下状态转移方程:

$$
\begin{aligned}
f[i][j][0] &= \max(f[i - 1][j][0], f[i - 1][j][1] + prices[i], f[i - 1][j][2] - prices[i]) \
f[i][j][1] &= \max(f[i - 1][j][1], f[i - 1][j - 1][0] - prices[i]) \
f[i][j][2] &= \max(f[i - 1][j][2], f[i - 1][j - 1][0] + prices[i])
\end{aligned}
$$

最终,我们需要返回 $f[n - 1][k][0]$,即在前 $n$ 天内,最多进行 $k$ 笔交易,且当前没有持有股票时的最大利润。

###python

class Solution:
    def maximumProfit(self, prices: List[int], k: int) -> int:
        n = len(prices)
        f = [[[0] * 3 for _ in range(k + 1)] for _ in range(n)]
        for j in range(1, k + 1):
            f[0][j][1] = -prices[0]
            f[0][j][2] = prices[0]
        for i in range(1, n):
            for j in range(1, k + 1):
                f[i][j][0] = max(
                    f[i - 1][j][0],
                    f[i - 1][j][1] + prices[i],
                    f[i - 1][j][2] - prices[i],
                )
                f[i][j][1] = max(f[i - 1][j][1], f[i - 1][j - 1][0] - prices[i])
                f[i][j][2] = max(f[i - 1][j][2], f[i - 1][j - 1][0] + prices[i])
        return f[n - 1][k][0]

###java

class Solution {
    public long maximumProfit(int[] prices, int k) {
        int n = prices.length;
        long[][][] f = new long[n][k + 1][3];
        for (int j = 1; j <= k; ++j) {
            f[0][j][1] = -prices[0];
            f[0][j][2] = prices[0];
        }
        for (int i = 1; i < n; ++i) {
            for (int j = 1; j <= k; ++j) {
                f[i][j][0] = Math.max(f[i - 1][j][0],
                    Math.max(f[i - 1][j][1] + prices[i], f[i - 1][j][2] - prices[i]));
                f[i][j][1] = Math.max(f[i - 1][j][1], f[i - 1][j - 1][0] - prices[i]);
                f[i][j][2] = Math.max(f[i - 1][j][2], f[i - 1][j - 1][0] + prices[i]);
            }
        }
        return f[n - 1][k][0];
    }
}

###cpp

class Solution {
public:
    long long maximumProfit(vector<int>& prices, int k) {
        int n = prices.size();
        long long f[n][k + 1][3];
        memset(f, 0, sizeof(f));
        for (int j = 1; j <= k; ++j) {
            f[0][j][1] = -prices[0];
            f[0][j][2] = prices[0];
        }

        for (int i = 1; i < n; ++i) {
            for (int j = 1; j <= k; ++j) {
                f[i][j][0] = max({f[i - 1][j][0], f[i - 1][j][1] + prices[i], f[i - 1][j][2] - prices[i]});
                f[i][j][1] = max(f[i - 1][j][1], f[i - 1][j - 1][0] - prices[i]);
                f[i][j][2] = max(f[i - 1][j][2], f[i - 1][j - 1][0] + prices[i]);
            }
        }

        return f[n - 1][k][0];
    }
};

###go

func maximumProfit(prices []int, k int) int64 {
n := len(prices)
f := make([][][3]int, n)
for i := range f {
f[i] = make([][3]int, k+1)
}

for j := 1; j <= k; j++ {
f[0][j][1] = -prices[0]
f[0][j][2] = prices[0]
}

for i := 1; i < n; i++ {
for j := 1; j <= k; j++ {
f[i][j][0] = max(f[i-1][j][0], f[i-1][j][1]+prices[i], f[i-1][j][2]-prices[i])
f[i][j][1] = max(f[i-1][j][1], f[i-1][j-1][0]-prices[i])
f[i][j][2] = max(f[i-1][j][2], f[i-1][j-1][0]+prices[i])
}
}

return int64(f[n-1][k][0])
}

###ts

function maximumProfit(prices: number[], k: number): number {
    const n = prices.length;
    const f: number[][][] = Array.from({ length: n }, () =>
        Array.from({ length: k + 1 }, () => Array(3).fill(0)),
    );

    for (let j = 1; j <= k; ++j) {
        f[0][j][1] = -prices[0];
        f[0][j][2] = prices[0];
    }

    for (let i = 1; i < n; ++i) {
        for (let j = 1; j <= k; ++j) {
            f[i][j][0] = Math.max(
                f[i - 1][j][0],
                f[i - 1][j][1] + prices[i],
                f[i - 1][j][2] - prices[i],
            );
            f[i][j][1] = Math.max(f[i - 1][j][1], f[i - 1][j - 1][0] - prices[i]);
            f[i][j][2] = Math.max(f[i - 1][j][2], f[i - 1][j - 1][0] + prices[i]);
        }
    }

    return f[n - 1][k][0];
}

###rust

impl Solution {
    pub fn maximum_profit(prices: Vec<i32>, k: i32) -> i64 {
        let n = prices.len();
        let k = k as usize;
        let mut f = vec![vec![vec![0i64; 3]; k + 1]; n];
        for j in 1..=k {
            f[0][j][1] = -(prices[0] as i64);
            f[0][j][2] = prices[0] as i64;
        }
        for i in 1..n {
            for j in 1..=k {
                f[i][j][0] = f[i - 1][j][0]
                    .max(f[i - 1][j][1] + prices[i] as i64)
                    .max(f[i - 1][j][2] - prices[i] as i64);
                f[i][j][1] = f[i - 1][j][1].max(f[i - 1][j - 1][0] - prices[i] as i64);
                f[i][j][2] = f[i - 1][j][2].max(f[i - 1][j - 1][0] + prices[i] as i64);
            }
        }
        f[n - 1][k][0]
    }
}

时间复杂度 $O(n \times k)$,空间复杂度 $O(n \times k)$。其中 $n$ 为数组 $\textit{prices}$ 的长度,而 $k$ 为最大交易次数。


有任何问题,欢迎评论区交流,欢迎评论区提供其它解题思路(代码),也可以点个赞支持一下作者哈 😄~

每日一题-买卖股票的最佳时机 V🟡

给你一个整数数组 prices,其中 prices[i] 是第 i 天股票的价格(美元),以及一个整数 k

你最多可以进行 k 笔交易,每笔交易可以是以下任一类型:

  • 普通交易:在第 i 天买入,然后在之后的第 j 天卖出,其中 i < j。你的利润是 prices[j] - prices[i]

  • 做空交易:在第 i 天卖出,然后在之后的第 j 天买回,其中 i < j。你的利润是 prices[i] - prices[j]

注意:你必须在开始下一笔交易之前完成当前交易。此外,你不能在已经进行买入或卖出操作的同一天再次进行买入或卖出操作。

通过进行 最多 k 笔交易,返回你可以获得的最大总利润。

 

示例 1:

输入: prices = [1,7,9,8,2], k = 2

输出: 14

解释:

我们可以通过 2 笔交易获得 14 美元的利润:
  • 一笔普通交易:第 0 天以 1 美元买入,第 2 天以 9 美元卖出。
  • 一笔做空交易:第 3 天以 8 美元卖出,第 4 天以 2 美元买回。

示例 2:

输入: prices = [12,16,19,19,8,1,19,13,9], k = 3

输出: 36

解释:

我们可以通过 3 笔交易获得 36 美元的利润:
  • 一笔普通交易:第 0 天以 12 美元买入,第 2 天以 19 美元卖出。
  • 一笔做空交易:第 3 天以 19 美元卖出,第 4 天以 8 美元买回。
  • 一笔普通交易:第 5 天以 1 美元买入,第 6 天以 19 美元卖出。

 

提示:

  • 2 <= prices.length <= 103
  • 1 <= prices[i] <= 109
  • 1 <= k <= prices.length / 2

状态机 DP:在 188 题的基础上增加一个状态(Python/Java/C++/Go)

如果只有普通交易,那么本题就是 188. 买卖股票的最佳时机 IV

回顾一下,188 题中的状态定义为:

  • $\textit{dfs}(i,j,0)$ 表示在 $\textit{prices}[0]$ 到 $\textit{prices}[i]$ 中完成至多 $j$ 笔交易,第 $i$ 天结束时未持有股票的最大利润。
  • $\textit{dfs}(i,j,1)$ 表示在 $\textit{prices}[0]$ 到 $\textit{prices}[i]$ 中完成至多 $j$ 笔交易,第 $i$ 天结束时持有股票的最大利润。

本题可以做空交易,也就是可以先加再减(普通交易是先减再加)。我们需要额外增加一类状态:

  • $\textit{dfs}(i,j,2)$ 表示在 $\textit{prices}[0]$ 到 $\textit{prices}[i]$ 中完成至多 $j$ 笔交易,第 $i$ 天结束时处于做空中(空头状态)的最大利润。

同样地,转移方程增加一条:

$$
\textit{dfs}(i,j,2) = \max(\textit{dfs}(i-1, j, 2), \textit{dfs}(i-1, j-1, 0)+\textit{prices}[i])
$$

分别对应继续处于做空中,或者在第 $i$ 天卖出股票,开始做空(开空)。

此外,对于 $\textit{dfs}(i,j,0)$,在计算最大值时额外考虑在第 $i$ 天买回股票(平空)的情况,即 $\textit{dfs}(i,j,2) - \textit{prices}[i]$。

注意本题可能算出负数,$\textit{memo}$ 数组可以初始化成 $\infty$ 或者 $-\infty$。

什么是做空?简单科普一下~

一、记忆化搜索

###py

class Solution:
    def maximumProfit(self, prices: List[int], k: int) -> int:
        # 在 [0,i] 中完成至多 j 笔交易,第 i 天【结束时】的状态为 end_state 的情况下的最大收益
        # 0=未持有股票,1=持有股票,2=做空中
        @cache  # 缓存装饰器,避免重复计算 dfs 的结果(记忆化)
        def dfs(i: int, j: int, end_state: int) -> int:
            if j < 0:
                return -inf
            if i < 0:
                return -inf if end_state else 0
            p = prices[i]
            if end_state == 0:
                return max(dfs(i - 1, j, 0), dfs(i - 1, j, 1) + p, dfs(i - 1, j, 2) - p)
            if end_state == 1:
                return max(dfs(i - 1, j, 1), dfs(i - 1, j - 1, 0) - p)
            return max(dfs(i - 1, j, 2), dfs(i - 1, j - 1, 0) + p)

        ans = dfs(len(prices) - 1, k, 0)
        dfs.cache_clear()  # 防止爆内存(一般来说,状态数达到 1e6 就需要写这个)
        return ans

###java

class Solution {
    private int[] prices;
    private long[][][] memo;

    public long maximumProfit(int[] prices, int k) {
        this.prices = prices;
        int n = prices.length;
        memo = new long[n][k + 1][3];
        for (long[][] mat : memo) {
            for (long[] row : mat) {
                Arrays.fill(row, Long.MIN_VALUE); // MIN_VALUE 表示还没有计算过
            }
        }
        return dfs(n - 1, k, 0);
    }

    // 在 [0,i] 中完成至多 j 笔交易,第 i 天【结束时】的状态为 endState 的情况下的最大收益
    // 0=未持有股票,1=持有股票,2=做空中
    private long dfs(int i, int j, int endState) {
        if (j < 0) {
            return Long.MIN_VALUE / 2; // 除 2 防止溢出
        }
        if (i < 0) {
            return endState > 0 ? Long.MIN_VALUE / 2 : 0;
        }
        if (memo[i][j][endState] != Long.MIN_VALUE) { // 之前计算过
            return memo[i][j][endState];
        }
        int p = prices[i];
        if (endState == 0) {
            return memo[i][j][endState] = Math.max(dfs(i - 1, j, 0), Math.max(dfs(i - 1, j, 1) + p, dfs(i - 1, j, 2) - p));
        }
        if (endState == 1) {
            return memo[i][j][endState] = Math.max(dfs(i - 1, j, 1), dfs(i - 1, j - 1, 0) - p);
        }
        return memo[i][j][endState] = Math.max(dfs(i - 1, j, 2), dfs(i - 1, j - 1, 0) + p);
    }
}

###cpp

class Solution {
public:
    long long maximumProfit(vector<int>& prices, int k) {
        int n = prices.size();
        vector memo(n, vector<array<long long, 3>>(k + 1, {LLONG_MIN, LLONG_MIN, LLONG_MIN})); // LLONG_MIN 表示还没有计算过

        // 在 [0,i] 中完成至多 j 笔交易,第 i 天【结束时】的状态为 end_state 的情况下的最大收益
        // 0=未持有股票,1=持有股票,2=做空中
        auto dfs = [&](this auto&& dfs, int i, int j, int end_state) -> long long {
            if (j < 0) {
                return LLONG_MIN / 2; // 除 2 防止溢出
            }
            if (i < 0) {
                return end_state ? LLONG_MIN / 2 : 0;
            }
            long long& res = memo[i][j][end_state]; // 注意这里是引用
            if (res != LLONG_MIN) { // 之前计算过
                return res;
            }
            int p = prices[i];
            if (end_state == 0) {
                return res = max({dfs(i - 1, j, 0), dfs(i - 1, j, 1) + p, dfs(i - 1, j, 2) - p});
            }
            if (end_state == 1) {
                return res = max(dfs(i - 1, j, 1), dfs(i - 1, j - 1, 0) - p);
            }
            return res = max(dfs(i - 1, j, 2), dfs(i - 1, j - 1, 0) + p);
        };

        return dfs(n - 1, k, 0);
    }
};

###go

func maximumProfit(prices []int, k int) int64 {
n := len(prices)
memo := make([][][3]int, n)
for i := range memo {
memo[i] = make([][3]int, k+1)
for j := range memo[i] {
memo[i][j] = [3]int{math.MinInt, math.MinInt, math.MinInt} // MinInt 表示还没有计算过
}
}

// 在 [0,i] 中完成至多 j 笔交易,第 i 天【结束时】的状态为 endState 的情况下的最大收益
// 0=未持有股票,1=持有股票,2=做空中
var dfs func(int, int, int) int
dfs = func(i, j, endState int) (res int) {
if j < 0 {
return math.MinInt / 2 // 防止溢出
}
if i < 0 {
if endState == 1 {
return math.MinInt / 2
}
return
}
ptr := &memo[i][j][endState]
if *ptr != math.MinInt { // 之前计算过
return *ptr
}
defer func() { *ptr = res }() // 记忆化
p := prices[i]
if endState == 0 {
return max(dfs(i-1, j, 0), dfs(i-1, j, 1)+p, dfs(i-1, j, 2)-p)
}
if endState == 1 {
return max(dfs(i-1, j, 1), dfs(i-1, j-1, 0)-p)
}
return max(dfs(i-1, j, 2), dfs(i-1, j-1, 0)+p)
}

return int64(dfs(n-1, k, 0))
}

二、1:1 翻译成递推

###py

class Solution:
    def maximumProfit(self, prices: List[int], k: int) -> int:
        n = len(prices)
        f = [[[-inf] * 3 for _ in range(k + 2)] for _ in range(n + 1)]
        for j in range(1, k + 2):
            f[0][j][0] = 0
        for i, p in enumerate(prices):
            for j in range(1, k + 2):
                f[i + 1][j][0] = max(f[i][j][0], f[i][j][1] + p, f[i][j][2] - p)
                f[i + 1][j][1] = max(f[i][j][1], f[i][j - 1][0] - p)
                f[i + 1][j][2] = max(f[i][j][2], f[i][j - 1][0] + p)
        return f[-1][-1][0]

###java

class Solution {
    public long maximumProfit(int[] prices, int k) {
        int n = prices.length;
        long[][][] f = new long[n + 1][k + 2][3];
        for (long[][] mat : f) {
            for (long[] row : mat) {
                Arrays.fill(row, Long.MIN_VALUE / 2);
            }
        }
        for (int j = 1; j <= k + 1; j++) {
            f[0][j][0] = 0;
        }
        for (int i = 0; i < n; i++) {
            int p = prices[i];
            for (int j = 1; j <= k + 1; j++) {
                f[i + 1][j][0] = Math.max(f[i][j][0], Math.max(f[i][j][1] + p, f[i][j][2] - p));
                f[i + 1][j][1] = Math.max(f[i][j][1], f[i][j - 1][0] - p);
                f[i + 1][j][2] = Math.max(f[i][j][2], f[i][j - 1][0] + p);
            }
        }
        return f[n][k + 1][0];
    }
}

###cpp

class Solution {
public:
    long long maximumProfit(vector<int>& prices, int k) {
        int n = prices.size();
        vector f(n + 1, vector<array<long long, 3>>(k + 2, {LLONG_MIN / 2, LLONG_MIN / 2, LLONG_MIN / 2}));
        for (int j = 1; j <= k + 1; j++) {
            f[0][j][0] = 0;
        }
        for (int i = 0; i < n; i++) {
            int p = prices[i];
            for (int j = 1; j <= k + 1; j++) {
                f[i + 1][j][0] = max({f[i][j][0], f[i][j][1] + p, f[i][j][2] - p});
                f[i + 1][j][1] = max(f[i][j][1], f[i][j - 1][0] - p);
                f[i + 1][j][2] = max(f[i][j][2], f[i][j - 1][0] + p);
            }
        }
        return f[n][k + 1][0];
    }
};

###go

func maximumProfit(prices []int, k int) int64 {
n := len(prices)
f := make([][][3]int, n+1)
for i := range f {
f[i] = make([][3]int, k+2)
for j := range f[i] {
f[i][j] = [3]int{math.MinInt / 2, math.MinInt / 2, math.MinInt / 2}
}
}
for j := 1; j <= k+1; j++ {
f[0][j][0] = 0
}
for i, p := range prices {
for j := 1; j <= k+1; j++ {
f[i+1][j][0] = max(f[i][j][0], f[i][j][1]+p, f[i][j][2]-p)
f[i+1][j][1] = max(f[i][j][1], f[i][j-1][0]-p)
f[i+1][j][2] = max(f[i][j][2], f[i][j-1][0]+p)
}
}
return int64(f[n][k+1][0])
}

三、空间优化

###py

class Solution:
    def maximumProfit(self, prices: List[int], k: int) -> int:
        f = [[-inf] * 3 for _ in range(k + 2)]
        for j in range(1, k + 2):
            f[j][0] = 0
        for p in prices:
            for j in range(k + 1, 0, -1):
                f[j][0] = max(f[j][0], f[j][1] + p, f[j][2] - p)
                f[j][1] = max(f[j][1], f[j - 1][0] - p)
                f[j][2] = max(f[j][2], f[j - 1][0] + p)
        return f[-1][0]

###java

class Solution {
    public long maximumProfit(int[] prices, int k) {
        long[][] f = new long[k + 2][3];
        for (int j = 1; j <= k + 1; j++) {
            f[j][1] = Long.MIN_VALUE / 2; // 防止溢出
        }
        f[0][0] = Long.MIN_VALUE / 2;
        for (int p : prices) {
            for (int j = k + 1; j > 0; j--) {
                f[j][0] = Math.max(f[j][0], Math.max(f[j][1] + p, f[j][2] - p));
                f[j][1] = Math.max(f[j][1], f[j - 1][0] - p);
                f[j][2] = Math.max(f[j][2], f[j - 1][0] + p);
            }
        }
        return f[k + 1][0];
    }
}

###cpp

class Solution {
public:
    long long maximumProfit(vector<int>& prices, int k) {
        vector<array<long long, 3>> f(k + 2, {LLONG_MIN / 2, LLONG_MIN / 2, LLONG_MIN / 2});
        for (int j = 1; j <= k + 1; j++) {
            f[j][0] = 0;
        }
        for (int p : prices) {
            for (int j = k + 1; j > 0; j--) {
                // f[j][0] = max({f[j][0], f[j][1] + p, f[j][2] - p}); 这种写法比下面的慢
                f[j][0] = max(f[j][0], max(f[j][1] + p, f[j][2] - p));
                f[j][1] = max(f[j][1], f[j - 1][0] - p);
                f[j][2] = max(f[j][2], f[j - 1][0] + p);
            }
        }
        return f[k + 1][0];
    }
};

###go

func maximumProfit(prices []int, k int) int64 {
f := make([][3]int, k+2)
for j := 1; j <= k+1; j++ {
f[j][1] = math.MinInt / 2
f[j][2] = math.MinInt / 2
}
f[0][0] = math.MinInt / 2
for _, p := range prices {
for j := k + 1; j > 0; j-- {
f[j][0] = max(f[j][0], f[j][1]+p, f[j][2]-p)
f[j][1] = max(f[j][1], f[j-1][0]-p)
f[j][2] = max(f[j][2], f[j-1][0]+p)
}
}
return int64(f[k+1][0])
}

复杂度分析

  • 时间复杂度:$\mathcal{O}(nk)$,其中 $n$ 是 $\textit{prices}$ 的长度。
  • 空间复杂度:$\mathcal{O}(k)$。

专题训练

见下面动态规划题单的「六、状态机 DP」。

分类题单

如何科学刷题?

  1. 滑动窗口与双指针(定长/不定长/单序列/双序列/三指针/分组循环)
  2. 二分算法(二分答案/最小化最大值/最大化最小值/第K小)
  3. 单调栈(基础/矩形面积/贡献法/最小字典序)
  4. 网格图(DFS/BFS/综合应用)
  5. 位运算(基础/性质/拆位/试填/恒等式/思维)
  6. 图论算法(DFS/BFS/拓扑排序/基环树/最短路/最小生成树/网络流)
  7. 动态规划(入门/背包/划分/状态机/区间/状压/数位/数据结构优化/树形/博弈/概率期望)
  8. 常用数据结构(前缀和/差分/栈/队列/堆/字典树/并查集/树状数组/线段树)
  9. 数学算法(数论/组合/概率期望/博弈/计算几何/随机算法)
  10. 贪心与思维(基本贪心策略/反悔/区间/字典序/数学/思维/脑筋急转弯/构造)
  11. 链表、树与回溯(前后指针/快慢指针/DFS/BFS/直径/LCA)
  12. 字符串(KMP/Z函数/Manacher/字符串哈希/AC自动机/后缀数组/子序列自动机)

我的题解精选(已分类)

欢迎关注 B站@灵茶山艾府

DP

解法:DP

和这类题之前的题目类似,维护 $f(i, j, t)$ 表示第 $i$ 天结束时,已经开启了 $j$ 次交易,且当前交易状态为 $t$ 的最大利润。$t = 0$ 表示当前不在交易中,$t = 1$ 表示正在进行普通交易(已经完成买入,还未卖出),$t = 2$ 表示正在进行做空交易(已经完成卖出,还未买入)。转移方程有以下几种情况:

  1. 第 $i$ 天不操作,则 $f(i, j, t) \xleftarrow{\min} f(i - 1, j, t)$。
  2. 第 $i$ 天开始一次普通交易,则 $f(i, j, 1) \xleftarrow{\min} f(i - 1, j - 1, 0) - p_i$。
  3. 第 $i$ 天开始一次做空交易,则 $f(i, j, 2) \xleftarrow{\min} f(i - 1, j - 1, 0) + p_i$。
  4. 第 $i$ 天结束一次普通交易,则 $f(i, j, 0) \xleftarrow{\min} f(i - 1, j, 1) + p_i$。
  5. 第 $i$ 天结束一次做空交易,则 $f(i, j, 0) \xleftarrow{\min} f(i - 1, j, 2) - p_i$。

复杂度 $\mathcal{O}(nk)$。

参考代码(c++)

class Solution {
public:
    long long maximumProfit(vector<int>& prices, int K) {
        int n = prices.size();

        const long long INF = 1e18;
        long long f[n + 1][K + 1][3];
        for (int i = 0; i <= n; i++) for (int k = 0; k <= K; k++) for (int t = 0; t < 3; t++) f[i][k][t] = -INF;
        f[0][0][0] = 0;

        for (int i = 1; i <= n; i++) for (int k = 0; k <= K; k++) {
            // 不交易
            for (int t = 0; t < 3; t++) f[i][k][t] = max(f[i][k][t], f[i - 1][k][t]);
            if (k > 0) {
                // 开始普通交易
                f[i][k][1] = max(f[i][k][1], f[i - 1][k - 1][0] - prices[i - 1]);
                // 开始做空交易
                f[i][k][2] = max(f[i][k][2], f[i - 1][k - 1][0] + prices[i - 1]);
            }
            // 结束普通交易
            f[i][k][0] = max(f[i][k][0], f[i - 1][k][1] + prices[i - 1]);
            // 结束做空交易
            f[i][k][0] = max(f[i][k][0], f[i - 1][k][2] - prices[i - 1]);
        }

        long long ans = -INF;
        for (int k = 0; k <= K; k++) ans = max(ans, f[n][k][0]);
        return ans;
    }
};
❌