1.题目链接:
2.题目描述:
给定一个整数数组 prices ,它的第 i 个元素 prices[i] 是一支给定的股票在第 i 天的价格。 设计一个算法来计算你所能获取的最大利润。你最多可以完成 k 笔交易。
注意:你不能同时参与多笔交易(你必须在再次购买前出售掉之前的股票)。
示例 1:
输入:k = 2, prices = [2,4,1]
输出:2
解释:
在第 1 天 (股票价格 = 2) 的时候买入,在第 2 天 (股票价格 = 4) 的时候卖出,这笔交易所能获得利润 = 4-2 = 2 。
示例 2:
输入:k = 2, prices = [3,2,6,5,0,3]
输出:7
解释:
在第 2 天 (股票价格 = 2) 的时候买入,在第 3 天 (股票价格 = 6) 的时候卖出, 这笔交易所能获得利润 = 6-2 = 4 。
随后,在第 5 天 (股票价格 = 0) 的时候买入,在第 6 天 (股票价格 = 3) 的时候卖出, 这笔交易所能
获得利润 = 3-0 = 3 。
3. 解法(动态规划):
算法思路:
1. 状态表示:
为了更加清晰的区分「买入」和「卖出」,我们换成「有股票」和「无股票」两个状态。
f[i][j] 表示:第 i 天结束后,完成了 j 笔交易,此时处于「有股票」状态的最大收益;
g[i][j] 表示:第 i 天结束后,完成了 j 笔交易,此时处于「无股票」状态的最大收益。
2. 状态转移方程:
对于 f [i][j] ,我们也有两种情况能在第 i 天结束之后,完成 j 笔交易,此时手里「有股
票」的状态:
i. | 在 i - 1 天的时候,手里「有股票」,并且交易了 j 次。在第 i 天的时候,啥也不干。 |
此时的收益为 f[i - 1][j] ;
ii. 在 i - 1 天的时候,手里「没有股票」,并且交易了 j 次。在第 i 天的时候,买了股票。那么 i 天结束之后,我们就有股票了。此时的收益为 g[i - 1][j] -
prices[i] ;
上述两种情况,我们需要的是「最大值」,因此 f 的状态转移方程为:
f[i][j] = max(f[i - 1][j], g[i - 1][j] - prices[i]) |
对于 g [i][j] ,我们有下面两种情况能在第 i 天结束之后,完成 j 笔交易,此时手里「没有
股票」的状态:
i. | 在 i - 1 天的时候,手里「没有股票」,并且交易了 j 次。在第 i 天的时候,啥也不 |
干。此时的收益为 g[i - 1][j] ;
ii. 在 i - 1 天的时候,手里「有股票」,并且交易了 j - 1 次。在第 i 天的时候,把股票卖了。那么 i 天结束之后,我们就交易了 j 次。此时的收益为 f[i - 1][j - 1] + prices[i] ;
上述两种情况,我们需要的是「最大值」,因此 g 的状态转移方程为:
g[i][j] = max(g[i - 1][j], f[i - 1][j - 1] + prices[i]) |
3. 初始化:
由于需要用到 i = 0 时的状态,因此我们初始化第一行即可。
◦ | 当处于第 0 天的时候,只能处于「买入过一次」的状态,此时的收益为 -prices[0] ,因 |
此 f[0][0] = - prices[0] 。
◦ | 为了取 max 的时候,一些不存在的状态「起不到干扰」的作用,我们统统将它们初始化为 - |
INF (用 INT_MIN 在计算过程中会有「溢出」的风险,这里 INF 折半取
0x3f3f3f3f ,足够小即可)
4. 填表顺序:
从上往下填每一行,每一行从左往右,两个表一起填。
5. 返回值:
返回处于卖出状态的最大值,但是我们也不知道是交易了几次,因此返回 g 表最后一行的最大
值。
优化点:
我们的交易次数是不会超过整个天数的一半的,因此我们可以先把 k 处理一下,优化一下问题的规
模:
k = min(k, n / 2) |
Java算法代码:
class Solution {
public int maxProfit(int k, int[] prices) {
// 1.预处理
int n = prices.length, INF = 0x3f3f3f3f;
k = Math.min(k,n/2);
int [][] f = new int[n][k+1];
int [][] g = new int[n][k+1];
// 初始化
for(int j = 0; j <= k; j++){
f[0][j] = g[0][j] = -INF;
}
f[0][0] = -prices[0];
g[0][0] = 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]);
g[i][j] = g[i - 1][j];
if(j >= 1){
g[i][j] = Math.max(g[i][j],f[i-1][j-1] + prices[i]);
}
}
}
int ret = 0;
for(int j = 0; j <= k; j++){
ret = Math.max(ret,g[n-1][j]);
}
return ret;
}
}
执行结果:
动态规划:
这里是上一个题的延申。思路是一摸一样的。只是长度变化。唯一的不同算是,判断合理性.来进行次数的优化。