买卖股票的最佳时机
分析
根据题意可知,我们只需要找出来一个最小价格的股票和一个最大价格的股票,并且最小价格的股票出现在最大价格的股票之前。
如果尝试使用暴力解法,时间复杂度为O(N^2),对于题目中给的长度,显然是会超时的,因此我们需要另寻它法。
我们可以使用一次遍历,在经过每个值时,计算出包含该值之前的股票的最低价格和最大利润,该种方法的时间复杂度为O(N),显然是可解的。
初始化:由于在确定价格最小值时涉及到min求最小值,而且根据题目所给价格的范围,我们可以使用 0x3f3f3f3f 来初始化minprice;根据题意,由于利润最小为0,不涉及到为负的情况,因此初始化 profit 为0即可。
填表顺序:从左到右。
返回值:profit
代码
class Solution {
public:
int maxProfit(vector<int>& prices) {
int n=prices.size();
int profit=0,minprice=0x3f3f3f3f;
for(int price:prices){
profit=max(profit,price-minprice);
minprice=min(price,minprice);
}
return profit;
}
};
买卖芯片的最佳时机
分析
根据题意,只需找到一个最低价格和一个最高价格,并且最低价格出现在最高价格之前,显然与上面的题意是一样的,解法显而易见也是一样的。
代码
class Solution {
public:
int bestTiming(vector<int>& prices) {
int n=prices.size();
int profit=0,minprice=0x3f3f3f3f;
for(int price:prices){
profit=max(profit,price-minprice);
minprice=min(price,minprice);
}
return profit;
}
};
买卖股票的最佳时机II
分析
如果以以往经验,对于本题,我们可以定义一个 dp 表,dp[i] 表示以 i 位置为结尾,能获取的最大利润,即:
由于题目中涉及到该天“买股票”和“卖股票”的问题,因此,仅仅使用一维 dp 是不能解决问题的,因此可以尝试使用二维 dp 来解决,dp[i][0] 表示手里没股票的时候能获取的最大利润,dp[i][1]表示手里有股票的时候能获取的最大利润(易知这里说的有股票指的是“一张股票”)。
状态转移方程:dp[i][0]=max(dp[i-1][0],dp[i-1][1]+prices[i])
dp[i][1]=max(dp[i-1][1],dp[i-1][0]-prices[i])
初始化:由于只会用到前一行的值,因此只需初始化第一行的值即可,dp[0][0]表示第1天手里没股票的最大利润,为0;dp[0][1]表示第一天手里有股票的最大利润,为 -prices[0]。
填表顺序:从上到下。
返回值:dp[n-1][0],因为最后一天手里有股票肯定是比最后一天没股票要亏。
代码
class Solution {
public:
int maxProfit(vector<int>& prices) {
int n=prices.size();
vector<vector<int>> dp(n,vector<int>(2));
dp[0][1]=-prices[0];
for(int i=1;i<n;i++){
dp[i][0]=max(dp[i-1][0],dp[i-1][1]+prices[i]);
dp[i][1]=max(dp[i-1][1],dp[i-1][0]-prices[i]);
}
return dp[n-1][0];
}
};
买卖股票的最佳时机含手续费
分析
如果以以往经验,对于本题,我们可以定义一个 dp 表,dp[i] 表示以 i 位置为结尾,能获取的最大利润,即:
由于题目涉及到该天“买股票”和“卖股票”这两种情况,显然只表示该天为结尾能获取的最大利润,是非常模糊的,那么问题也得不到解决。
因此考虑使用二维 dp 来解决问题,dp[i][0]表示第 i 天手里没股票的时候能获取的最大利润;dp[i][1]表示第 i 天手里有股票的时候能获取的最大利润(显然,根据题意,这里所说的有股票指的是“一张股票”)。
状态转移方程:dp[i][0]=max(dp[i-1][0],dp[i-1][1]+prices[i]-fee);
dp[i][1]=max(dp[i-1][1],dp[i-1][0]-prices[i]);
初始化:由于只会用到前一行的值,因此只需初始化第一行的值即可,dp[0][0]表示第1天手里没股票的最大利润,为0;dp[0][1]表示第一天手里有股票的最大利润,为 -prices[0]。
填表顺序:从上到下。
返回值:dp[n-1][0],因为最后一天手里有股票肯定是比最后一天没股票要亏。
解法一
代码
class Solution {
public:
int maxProfit(vector<int>& prices, int fee) {
int n=prices.size();
vector<vector<int>> dp(n,vector<int>(2));
//dp[i][0] 表示没股票的最大利润
//dp[i][1] 表示有股票的最大利润
dp[0][1]=-prices[0];
for(int i=1;i<n;i++){
dp[i][0]=max(dp[i-1][0],dp[i-1][1]+prices[i]-fee);
dp[i][1]=max(dp[i-1][1],dp[i-1][0]-prices[i]);
}
return dp[n-1][0];
}
};
解法二
根据解法一的状态转移方程可知,每一次填写只会用到前一行的两个值,因此我们可以只定义几个变量来解决问题。
代码
class Solution {
public:
int maxProfit(vector<int>& prices, int fee) {
int n=prices.size();
int a=0,b=-prices[0];
//a 表示没股票的最大利润
//b 表示有股票的最大利润
for(int i=1;i<n;i++){
int a1=max(a,b+prices[i]-fee);
int b1=max(b,a-prices[i]);
a=a1,b=b1;
}
return a;
}
};
解法三(贪心)
仔细分析前两种解法,不难发现,都是在卖出股票的时候进行决策优劣的,我们不妨尝试在买入股票的时候进行优劣的决策。
定义buy表示买入价格(含手续费),当某天价格+手续费 < buy 的时候,表示我们可以用更低成本卖股票,则更新 buy=prices[i]+fee。
定义profit表示能获取的最大利润,当prices[i] > buy的时候,我们会卖出股票,但是这里需要注意的是,如果我们这个时候卖出股票,可能不是最优的,因为可能之后的价格更高,即一张股票可以赚更高的利润,因此我们可以尝试以下策略:先卖掉,并把 buy 更新为 prices[i],那么在之后遇到更高价格之后,只需加上差价即可,即:我们是提供了一种“可以反悔”的操作。
代码
class Solution {
public:
int maxProfit(vector<int>& prices, int fee) {
int n=prices.size();
int buy=prices[0]+fee;
int profit=0;
for(int i=1;i<n;i++){
if(prices[i]+fee < buy)
buy=prices[i]+fee;
else if(prices[i] > buy){
profit+=prices[i]-buy;
buy=prices[i];
}
}
return profit;
}
};
买卖股票的最佳时期含冷冻期
分析
如果以以往经验,对于本题,我们可以定义一个 dp 表,dp[i] 表示以 i 位置为结尾,能获取的最大利润,即:
由于题目涉及到该天“买股票”,“卖股票”和“冷冻期”这三种情况,显然只表示该天为结尾能获取的最大利润,是非常模糊的,那么问题也得不到解决。
因此考虑使用二维 dp 来解决问题,dp[i][0]表示第 i 天手里有股票的时候能获取的最大利润(显然,根据题意,这里所说的有股票指的是“一张股票”);dp[i][1]表示手里没股票,当天处于冷冻期的最大利润;dp[i][2]表示手里没股票,当天不处于冷冻期的时候所能获取的最大利润。
状态转移方程:dp[i][0]=max(dp[i-1][0],dp[i-1][2]-prices[i]);
dp[i][1]=dp[i-1][0]+prices[i];
dp[i][2]=max(dp[i-1][1],dp[i-1][2]);
初始化:根据状态转移方程可知,每次填值只会用到上一行的值,因此只需初始化第一行即可,
dp[0][0]= -prices[0],dp[0][1]=0,dp[0][2]=0。
填表顺序:从上到下。
返回值:max(dp[n-1][1],dp[n-1][2]).因为最后一天手里有股票肯定是比最后一天手里没股票要亏的.
解法一
代码
class Solution {
public:
int maxProfit(vector<int>& prices) {
int n=prices.size();
vector<vector<int>> dp(n,vector<int>(3));
dp[0][0]=-prices[0];
//dp[i][0] 手里有股票的最大利润
//dp[i][1] 手里没股票,处于冷冻期的最大利润
//dp[i][2] 手里没股票,不处于冷冻期的最大利润
for(int i=1;i<n;i++){
dp[i][0]=max(dp[i-1][0],dp[i-1][2]-prices[i]);
dp[i][1]=dp[i-1][0]+prices[i];
dp[i][2]=max(dp[i-1][1],dp[i-1][2]);
}
return max(dp[n-1][1],dp[n-1][2]);
}
};
解法二
根据解法一可知,每次填值只会用到上一行的三个值,因此可以尝试用几个变量来解决,时间复杂度为O(N)。
代码
class Solution {
public:
int maxProfit(vector<int>& prices) {
int n=prices.size();
int a=-prices[0],b=0,c=0;
//a 手里有股票的最大利润
//b 手里没股票,处于冷冻期的最大利润
//c 手里没股票,不处于冷冻期的最大利润
for(int i=1;i<n;i++){
int a1=max(a,c-prices[i]);
int b1=a+prices[i];
int c1=max(b,c);
a=a1,b=b1,c=c1;
}
return max(b,c);
}
};
买卖股票的最佳时机III
分析
如果以以往经验,对于本题,我们可以定义一个 dp 表,dp[i] 表示以 i 位置为结尾,能获取的最大利润,即:
由于该题涉及“买股票”,“卖股票”和“交易次数”的问题,显然使用一维dp是不能解决问题的。
我们可以使用三维 dp 来解决,dp[i][j][k],i 表示第 i 天,j 表示处于手里有股票还是没股票,k 表示已交易次数,这样看来,三维的有点头疼。
我们不妨可以使用两个二维dp来解决,f[i][j]表示手里没股票,已交易 j 次能获取的最大利润,g[i][j]表示手里有股票,已交易 j 次能获取的最大利润。
状态转移方程:f[i][j]=max(f[i-1][[j],g[i-1][j-1]+prices[i])【注意:使用g[i-1][j-1]须满足 j >=1】
g[i][j]=max(g[i-1][j],f[i-1][j]-prices[i]).
初始化:对于f[i][j]来说,会用到上一行的值,因此只需初始化第一行即可,由于第一行表示第一天,因此最多交易0次,对于高于0次的情况并不可能出现,初始化为 -0x3f3f3f3f.
对于g[i][j]来说,会用到上一行的值,也可能用到前一行前一列的值(左上方)【因此不需要初始化】
填表顺序:从上到下,从左到右。
返回值:f[i][j]里面的最大值。
代码
class Solution {
public:
int maxProfit(vector<int>& prices) {
int n=prices.size();
vector<vector<int>> f(n,vector<int>(3,-0x3f3f3f3f));
vector<vector<int>> g(n,vector<int>(3,-0x3f3f3f3f));
//f[i][j] 表示没股票的时候,已经交易j次,获取的最大利润
//g[i][j] 表示有股票的时候,已经交易j次,获取的最大利润
f[0][0]=0,g[0][0]=-prices[0];
for(int i=1;i<n;i++){
for(int j=0;j<=2;j++){
f[i][j]=f[i-1][j];
if(j>=1)
f[i][j]=max(f[i][j],g[i-1][j-1]+prices[i]);
g[i][j]=max(g[i-1][j],f[i-1][j]-prices[i]);
}
}
return *max_element(f[n-1].begin(),f[n-1].end());
}
};
买卖股票的最佳时机IV
分析
这道题与上一道题相比,差异之处为:上一道题限制交易次数最多为两次,本题限制交易次数最多为k次,所以解法和代码同上一道题几乎 如出一辙。
如果以以往经验,对于本题,我们可以定义一个 dp 表,dp[i] 表示以 i 位置为结尾,能获取的最大利润,即:
由于该题涉及“买股票”,“卖股票”和“交易次数”的问题,显然使用一维dp是不能解决问题的。
我们可以使用三维 dp 来解决,dp[i][j][k],i 表示第 i 天,j 表示处于手里有股票还是没股票,k 表示已交易次数,这样看来,三维的有点头疼。
我们不妨可以使用两个二维dp来解决,f[i][j]表示手里没股票,已交易 j 次能获取的最大利润,g[i][j]表示手里有股票,已交易 j 次能获取的最大利润。
状态转移方程:f[i][j]=max(f[i-1][[j],g[i-1][j-1]+prices[i])【注意:使用g[i-1][j-1]须满足 j >=1】
g[i][j]=max(g[i-1][j],f[i-1][j]-prices[i]).
初始化:对于f[i][j]来说,会用到上一行的值,因此只需初始化第一行即可,由于第一行表示第一天,因此最多交易0次,对于高于0次的情况并不可能出现,初始化为 -0x3f3f3f3f.
对于g[i][j]来说,会用到上一行的值,也可能用到前一行前一列的值(左上方)【因此不需要初始化】
填表顺序:从上到下,从左到右。
返回值:f[i][j]里面的最大值。
代码
class Solution {
public:
int maxProfit(int k, vector<int>& prices) {
int n=prices.size();
vector<vector<int>> f(n,vector<int>(k+1,-0x3f3f3f3f));
vector<vector<int>> g(n,vector<int>(k+1,-0x3f3f3f3f));
//f[i][j] 表示没股票的时候,已经交易j次,获取的最大利润
//g[i][j] 表示有股票的时候,已经交易j次,获取的最大利润
f[0][0]=0,g[0][0]=-prices[0];
for(int i=1;i<n;i++){
for(int j=0;j<=k;j++){
f[i][j]=f[i-1][j];
if(j>=1)
f[i][j]=max(f[i][j],g[i-1][j-1]+prices[i]);
g[i][j]=max(g[i-1][j],f[i-1][j]-prices[i]);
}
}
return *max_element(f[n-1].begin(),f[n-1].end());
}
};