这是一个系列,也是dp经典题,特别写篇博客。
leetcode 121 买卖股票的最佳时机 简单
只能买卖一次股票,那只需要从第一天开始一次遍历,不停地更新买的最低价和能赚到的最大利润即可。
参考代码
class Solution {
public:
int maxProfit(vector<int>& prices) {
int ans=0,low=999999;
for(int i=0;i<prices.size();i++)
{
if(prices[i]<low)
low=prices[i];
else
ans=max(ans,prices[i]-low);
}
return ans;
}
};
leetcode122 买卖股票的最佳时机II 简单
可以多次买卖股票,但同时只能持有一支股票
只需要贪心即可,不停地扩大利润。
class Solution {
public:
int maxProfit(vector<int>& prices) {
int last=prices[0],ans=0;
for(int i=1;i<prices.size();i++)
{
if(prices[i]<last)
last=prices[i];
else
{
ans+=prices[i]-last;
last=prices[i];
}
}
return ans;
}
};
当然,差分数组也是可以的,但理解有些困难,因为它并没有模拟买入卖出的过程,但其本质和上述代码是一样的。
class Solution {
public:
int maxProfit(vector<int>& prices) {
int ans=0;
for (int i=1;i<prices.size();i++)
ans+=max(0,prices[i]-prices[i-1]);
return ans;
}
};
当然,还可以用动态规划,虽然在这道题上有些多余,但可以用来为接下来的题做铺垫。
class Solution {
public:
int maxProfit(vector<int>& prices) {
int n=prices.size();
vector<vector<int>> dp(2,vector<int>(n));
dp[0][0]=-prices[0];
for(int i=1;i<n;i++)
{
dp[0][i]=max(dp[0][i-1],dp[1][i-1]-prices[i]);
dp[1][i]=max(dp[1][i-1],dp[0][i-1]+prices[i]);
}
return dp[1][n-1];
}
};
力扣123 买卖股票的最佳时机III 困难
一下子从简单难度跃升为困难。
最多可以完成两笔交易,但还是只能同时最多拥有一支股票。
有五种状态:未卖出且不持有股票的(状态0),买过一次但还没脱手的(状态1),买卖一次的(状态2),买卖一次后又买了但没脱手(状态3),两次买卖全部完成(状态4)。
从未卖出且不持有(永不入坑)
根据经验,我们可以得到状态转移方程,看上去完美无缺,不是么?
问题来了,两次买卖完成,至少是在第4天,即i=3才能完成,然而在循环中,第二次就会有值了。与现实不符!
错误代码样例:
class Solution {
public:
int maxProfit(vector<int>& prices) {
int n=prices.size();
vector<vector<vector<int>>> dp(3,vector<vector<int>>(2,vector<int>(n)));
dp[0][0][0]=-prices[0];
for(int i=1;i<n;i++)
{
// dp[2][0][i]=max(dp[2][0][i-1],dp[2][1][i]-prices[i]);
dp[2][1][i]=max(dp[2][1][i-1],dp[1][0][i-1]+prices[i]);
dp[1][0][i]=max(dp[1][0][i-1],dp[1][1][i]-prices[i]);
dp[1][1][i]=max(dp[1][1][i-1],dp[0][0][i-1]+prices[i]);
dp[0][1][i]=max(0,dp[0][1][i-1]);
dp[0][0][i]=max(dp[0][0][i-1],-prices[i]);
}
return max(dp[0][1][n-1],max(dp[1][1][n-1],dp[2][1][n-1]));
}
};
力扣309 最佳买卖股票时机含冷冻期 中等
最多有一支股票,但卖出后必须停手一天(冷冻期)。
依然是动态规划。
思考一下状态:分别有持有股票,不持有股票处于冷冻期以及不持有股票不处于冷冻期。
有了经验,dp[i][j]表示i状态第j天时的最大值。
那么不妨设持有股票为状态0,不持有股票处于冷冻期为状态1,不持有股票不处于冷冻期为状态2。
dp[0][i]表示在第i天持有股票,那么它可以是第i-1天买来在第i天未售出,或是第i天刚买的。如果是第i天买的,要求第i-1天不持有股票不处于冷冻期。那么状态转移方程显而易见:dp[0][i]=max(dp[0][i-1],dp[2][i-1])
dp[1][i]表示在第i天不持有股票且处于冷冻期,那么它只可能是当天卖了股票,那么在第i-1天他一定持有股票,那么状态转移方程也很明显:dp[1][i]=dp[0][i-1]+prices[i]
dp[2][i]表示在第天不持有股票不处于冷冻期,它可能是刚从冷冻期解冻出来,也可能是之前就已经解冻但没有购买股票,那么状态方程也可以写出来了:dp[2][i]=max(dp[1][i-1],dp[0][i-1])
根据上述分析,在第0天,dp[0][0]应该是买入第0天的股票即-prices[i],dp[1][i]是不可能存在的状态,即0,dp[2][i]为0
而最后我们只需要求最后一天三种状态的最大值即可。(事实上第0种状态即持有股票对答案是无意义的,因为此时他手持股票,必然亏本。)
参考代码
class Solution {
public:
int maxProfit(vector<int>& prices) {
int n=prices.size();
vector<vector<int>> dp(3,vector<int>(n));
dp[0][0]=-prices[0];
for(int i=1;i<n;i++)
{
dp[0][i]=max(dp[0][i-1],dp[2][i-1]-prices[i]);
dp[1][i]=dp[0][i-1]+prices[i];
dp[2][i]=max(dp[1][i-1],dp[2][i-1]);
}
return max(dp[0][n-1],max(dp[1][n-1],dp[2][n-1]));
}
};