这道题是LeetCode 121 和 122 的升级版,题意也差不多,也是给出一个数组,每个元素代表每天买入卖出石头的价格,不能在同一天买入与卖出,要先卖出已有的石头才能重新买入,求最大利润。这次限制买入卖出的次数为2。
这道题比较简单的方法是枚举每个i,即0 <= i <= n-1 , 分别求出[0,i] 和 [i+1, n-1] 两个区间各买卖一次的最大利润,求和。这样和的最大值就是最大利润。时间复杂度为O(n2)。
但这样会超时。于是还有另外一种动态规划的做法,我们可用g[i] 表示 区间[ 0, i] 买卖最多一次的最大值,用f[i] 表示区间[ i, n-1] 买卖最多一次的最大值。对于g[i] , f[i] 有递推公式:
当 i = 0 时, g[i] = 0 ;
当 1 <= i <= n-1 时, g[i] = max(g[i-1], prices[i] - ming) , 其中ming 为区间[0, i-1] 中prices的最小值。
当 i = n-1 时, f[i] = 0 ;
当 0 <= i <= n-2 时, f[i] = max(g[i+1], maxf - prices[i]), 其中maxf 为区间[i+1, n-1] 中prices 的最大值。
计算g[i] , f[i] 的时间复杂度为O(n)。
则利润的最大值要么是g[n-1], 要么是g[i] + f[i+1] 的最大值。计算的时间复杂度为O(n)。
所以总的复杂度为O(n),代码如下:
# include <iostream>
# include <cstdio>
# include <cstring>
# include <vector>
using namespace std ;
const int maxn = 100000 + 50 ;
const int INF = 10000000 ;
int f[maxn], g[maxn] ;
class Solution {
public:
int maxProfit(vector<int>& prices) ;
};
int Solution::maxProfit(vector<int>& prices) {
if (prices.size() == 0) return 0 ;
memset(f, 0, sizeof(f)) ;
memset(g, 0, sizeof(g)) ;
int n = prices.size() ;
int maxf = -INF ;
int ming = INF ;
g[0] = 0 ;
ming = prices[0] ;
for (int i=1; i<n; i++) {
g[i] = max(g[i-1], prices[i]-ming) ;
if (prices[i] < ming) ming = prices[i] ;
}
f[n-1] = 0 ;
maxf = prices[n-1] ;
for (int i=n-2; i>=0; i--) {
f[i] = max(f[i+1], maxf-prices[i]) ;
if (prices[i] > maxf) maxf = prices[i] ;
}
int ans = max(g[n-1], f[0]) ;
for (int i=0; i<n-1; i++) {
ans = max(ans, g[i]+f[i+1]) ;
}
// cout << g[2] << " " << f[4] << endl ;
return ans ;
}