1 买卖股票最佳时机
给定一个数组
prices
,它的第i
个元素prices[i]
表示一支给定股票第i
天的价格。你只能选择 某一天 买入这只股票,并选择在 未来的某一个不同的日子 卖出该股票。设计一个算法来计算你所能获取的最大利润。
返回你可以从这笔交易中获取的最大利润。如果你不能获取任何利润,返回
0
。一次买入卖出
1.1 递归关系
设置二维数据dp,dp[i][0]代表第i天手上有股票的最大收益,可能是之前买的,也可能是今天买的,dp[i][1]代表第i天手上无股票的最大收益,可能本来就没有买,可能是卖了。
那么,当前日有股票的最大收益两种情况:
昨天也有股票的最大收益
今日第一次入股的成本,之前没有买过
而当前日无股票的最大收益:
- 昨天也无股票的最大收益
- 今日卖出的股价,加上昨天手上有股票的收益
-
当日有股票的时候
d p [ i ] [ 0 ] = m a x ( d p [ i − 1 ] [ 0 ] , − p r i c e [ i ] ) dp[i][0] = max(dp[i-1][0],-price[i]) dp[i][0]=max(dp[i−1][0],−price[i]) -
当日无股票的时候
d p [ i ] [ 1 ] = m a x ( d p [ i − 1 ] [ 1 ] , p r i c e [ i ] + d p [ i − 1 ] [ 0 ] ) dp[i][1] = max(dp[i-1][1],price[i]+dp[i-1][0]) dp[i][1]=max(dp[i−1][1],price[i]+dp[i−1][0])
price[i]+dp[i-1][0]
代表今日卖出,dp[i-1][0]
是昨日持有股票的收益,加上今天卖出的价格price[i]
才是最终今天卖出的收益
1.2 边界条件
-
第一日有股票
d p [ 0 ] [ 0 ] = − p r i c e [ 0 ] dp[0][0] = -price[0] dp[0][0]=−price[0] -
第一日无股票
d p [ 0 ] [ 1 ] = 0 dp[0][1] = 0 dp[0][1]=0
1.3 代码
class Solution {
public int maxProfit(int[] prices) {
if(prices==null||prices.length==0){
return 0;
}
int len = prices.length;
int[][]dp = new int[len][2];
//初始化
dp[0][0] = -prices[0];
dp[0][1] = 0;
//开始遍历
for(int i = 1;i<len;i++){
dp[i][0] = Math.max(dp[i-1][0],-prices[i]);
dp[i][1] = Math.max(dp[i-1][1],dp[i-1][0]+prices[i]);
}
return Math.max(dp[len-1][0],dp[len-1][1]);
}
}
2 买卖股票最佳时机II
与第一题只改动了一行代码!
给你一个整数数组
prices
,其中prices[i]
表示某支股票第i
天的价格。在每一天,你可以决定是否购买和/或出售股票。你在任何时候 最多 只能持有 一股 股票。你也可以先购买,然后在 同一天 出售。
返回 你能获得的 最大 利润 。
区别:能买入卖出股票很多次!
2.1 递归关系
设置二维数据dp,dp[i][0]代表第i天手上有股票的最大收益,可能是之前买的,也可能是今天买的,dp[i][1]代表第i天手上无股票的最大收益,可能本来就没有买,可能是卖了。
那么,当前日有股票的最大收益两种情况:
昨天也有股票的最大收益
今日入股的成本,加上昨天手上无股票的收益
而当前日无股票的最大收益:
- 昨天也无股票的最大收益
- 今日卖出的股价,加上昨天手上有股票的收益
-
当日有股票的时候
d p [ i ] [ 0 ] = m a x ( d p [ i − 1 ] [ 0 ] , − p r i c e [ i ] + d p [ i − 1 ] [ 1 ] ) dp[i][0] = max(dp[i-1][0],-price[i]+dp[i-1][1]) dp[i][0]=max(dp[i−1][0],−price[i]+dp[i−1][1]) -
当日无股票的时候
d p [ i ] [ 1 ] = m a x ( d p [ i − 1 ] [ 1 ] , p r i c e [ i ] + d p [ i − 1 ] [ 0 ] ) dp[i][1] = max(dp[i-1][1],price[i]+dp[i-1][0]) dp[i][1]=max(dp[i−1][1],price[i]+dp[i−1][0])
price[i]+dp[i-1][0]
代表今日卖出,dp[i-1][0]
是昨日持有股票的收益,加上今天卖出的价格price[i]
才是最终今天卖出的收益
2.2 边界条件
-
第一日有股票
d p [ 0 ] [ 0 ] = − p r i c e [ 0 ] dp[0][0] = -price[0] dp[0][0]=−price[0] -
第一日无股票
d p [ 0 ] [ 1 ] = 0 dp[0][1] = 0 dp[0][1]=0
2.3 代码
class Solution {
public int maxProfit(int[] prices) {
if(prices==null||prices.length==0){
return 0;
}
int len = prices.length;
int[][]dp = new int[len][2];
//初始化
dp[0][0] = -prices[0];
dp[0][1] = 0;
//开始遍历
for(int i = 1;i<len;i++){
dp[i][0] = Math.max(dp[i-1][0],-prices[i]+dp[i-1][1]);
dp[i][1] = Math.max(dp[i-1][1],dp[i-1][0]+prices[i]);
}
return Math.max(dp[len-1][0],dp[len-1][1]);
}
}
3 买卖股票最佳时机III
给定一个数组,它的第
i
个元素是一支给定的股票在第i
天的价格。设计一个算法来计算你所能获取的最大利润。你最多可以完成 两笔 交易。
**注意:**你不能同时参与多笔交易(你必须在再次购买前出售掉之前的股票)。
最多买入卖出两次!设置dp[len][4]数组,表示第一次第二次持有与未持有的最大收益
3.1 递归关系
设置二维数据dp,
dp[i][0]代表第i天第一次买卖手上有股票的最大收益,可能是之前买的,也可能是今天第一次买的,
dp[i][1]代表第i天第一次买卖手上无股票的最大收益,可能本来就没有买,可能是卖了;
dp[i][2]代表第i天第二次买卖手上有股票的最大收益,可能是之前第二次买的,也可能是今天第二次买,
dp[i][1]代表第i天第二次买卖手上无股票的最大收益,可能早就卖了第二次,可能是今天卖了;
那么,当前日第一次有股票的最大收益两种情况:
昨天也有股票的最大收益
今日第一次入股
而当前日第一次无股票的最大收益:
昨天也无股票的最大收益
今日卖出的股价,加上昨天手上有股票的收益
当前日第二次有股票的最大收益的两天情况:
- 昨天第二次有股票的收益(今天之前就已经买了第二次)
- 昨天第一次卖股票的收益-今天第二次入股的成本(今天买第二次)
而当前日第二次无股票的最大收益:
- 昨天第二次无股票的收益(今天之前就已经卖了第二次)
- 昨天第二次有股票的收益+今日股价(今日卖出第二次)
-
当日第一次有股票的时候
d p [ i ] [ 0 ] = m a x ( d p [ i − 1 ] [ 0 ] , − p r i c e [ i ] ) dp[i][0] = max(dp[i-1][0],-price[i]) dp[i][0]=max(dp[i−1][0],−price[i]) -
当日第一次无股票的时候
d p [ i ] [ 1 ] = m a x ( d p [ i − 1 ] [ 1 ] , p r i c e [ i ] + d p [ i − 1 ] [ 0 ] ) dp[i][1] = max(dp[i-1][1],price[i]+dp[i-1][0]) dp[i][1]=max(dp[i−1][1],price[i]+dp[i−1][0]) -
当日第二次有股票的时候
d p [ i ] [ 2 ] = m a x ( d p [ i − 1 ] [ 2 ] , d p [ i − 1 ] [ 1 ] − p r i c e [ i ] ) dp[i][2] = max(dp[i-1][2],dp[i-1][1]-price[i]) dp[i][2]=max(dp[i−1][2],dp[i−1][1]−price[i]) -
当日第二次无股票的时候
d p [ i ] [ 3 ] = m a x ( d p [ i − 1 ] [ 3 ] , d p [ i − 1 ] [ 2 ] + p r i c e [ i ] ) dp[i][3] = max(dp[i-1][3],dp[i-1][2]+price[i]) dp[i][3]=max(dp[i−1][3],dp[i−1][2]+price[i])
3.2 边界条件
-
第一日第一次有股票
d p [ 0 ] [ 0 ] = − p r i c e [ 0 ] dp[0][0] = -price[0] dp[0][0]=−price[0] -
第一日第一次无股票
d p [ 0 ] [ 1 ] = 0 dp[0][1] = 0 dp[0][1]=0 -
第一日第二次有股票
d p [ 0 ] [ 0 ] = − p r i c e [ 0 ] dp[0][0] = -price[0] dp[0][0]=−price[0] -
第一日第二次无股票
d p [ 0 ] [ 0 ] = 0 dp[0][0] = 0 dp[0][0]=0
3.3 代码
class Solution {
public int maxProfit(int[] prices) {
if(prices==null||prices.length==0){
return 0;
}
int len = prices.length;
int[][]dp = new int[len][4];
//初始化
dp[0][0] = -prices[0];
dp[0][1] = 0;
dp[0][2] = -prices[0];
dp[0][3] = 0;
//开始遍历
for(int i = 1;i<len;i++){
//第一次有股票
dp[i][0] = Math.max(dp[i-1][0],-prices[i]);
//第一次无股票
dp[i][1] = Math.max(dp[i-1][1],dp[i-1][0]+prices[i]);
//第二次有股票
dp[i][2] = Math.max(dp[i-1][2],dp[i-1][1]-prices[i]);
//第二次无股票
dp[i][3] = Math.max(dp[i-1][3],dp[i-1][2]+prices[i]);
}
return Math.max(Math.max(dp[len-1][0],dp[len-1][1]),Math.max(dp[len-1][2],dp[len-1][3]));
}
}
4 买卖股票最佳时机IV
给你一个整数数组
prices
和一个整数k
,其中prices[i]
是某支给定的股票在第i
天的价格。设计一个算法来计算你所能获取的最大利润。你最多可以完成
k
笔交易。也就是说,你最多可以买k
次,卖k
次。最多可以买卖k次!
4.1 递归关系
设置二维数据dp[len][2k],dp[i][0]代表第i天手上有股票的最大收益,可能是之前买的,也可能是今天买的,dp[i][1]代表第i天手上无股票的最大收益,可能本来就没有买,可能是卖了;dp[i][2]代表第二次买卖手上有股票的最大收益,dp[i][3]代表第二次买卖手上无股票的最大收益;
…
递推得知
dp[i][(k-1)*2]代表第k次买卖手上有股票的最大收益
dp[i][(k-1)*2+1]代表第k次买卖手上无股票的最大收益
-
当日有股票的时候
- 前一日有股票的最大收益
- 前一日无股票的收益-当日入股市的成本**(第一次入股只需成本)**
d p [ i ] [ ( k − 1 ) ∗ 2 ] = m a x ( d p [ i − 1 ] [ ( k − 1 ) ∗ 2 ] , − p r i c e [ i ] + i f ( k − 1 = = 0 ) ? 0 : d p [ i − 1 ] [ ( k − 1 ) ∗ 2 − 1 ] ) dp[i][(k-1)*2] = max(dp[i-1][(k-1)*2],-price[i]+if(k-1==0)?0:dp[i-1][(k-1)*2-1]) dp[i][(k−1)∗2]=max(dp[i−1][(k−1)∗2],−price[i]+if(k−1==0)?0:dp[i−1][(k−1)∗2−1])
-
当日无股票的时候
- 前一个无股票的最大收益
- 前一日有股票的最大收益+当日股市的股价
d p [ i ] [ ( k − 1 ) ∗ 2 + 1 ] = m a x ( d p [ i − 1 ] [ ( k − 1 ) ∗ 2 + 1 ] , d p [ i − 1 ] [ ( k − 1 ) ∗ 2 ] + p r i c e [ i ] ) dp[i][(k-1)*2+1] = max(dp[i-1][(k-1)*2+1],dp[i-1][(k-1)*2]+price[i]) dp[i][(k−1)∗2+1]=max(dp[i−1][(k−1)∗2+1],dp[i−1][(k−1)∗2]+price[i])
price[i]+dp[i-1][0]
代表今日卖出,dp[i-1][0]
是昨日持有股票的收益,加上今天卖出的价格price[i]
才是最终今天卖出的收益
2.2 边界条件
-
第一日有股票(不管买入卖出折腾多少次)
d p [ 0 ] [ ( k − 1 ) ∗ 2 ] = − p r i c e [ 0 ] dp[0][(k-1)*2] = -price[0] dp[0][(k−1)∗2]=−price[0] -
第一日无股票(不管买入卖出折腾多少次)
d p [ 0 ] [ ( k − 1 ) ∗ 2 + 1 ] = 0 dp[0][(k-1)*2+1] = 0 dp[0][(k−1)∗2+1]=0
2.3 代码
public class Solution {
public int maxProfit(int k, int[] prices) {
int n = prices.length;
int[][] dp = new int[n][2 * k];
//初始化
for(int index=0;index<2*k;index++){
if(index%2==0){
dp[0][index] = -prices[0];
}else{
dp[0][index] = 0;
}
}
for (int i = 1; i < n; i++) {
for (int j = 0; j < k; j++) {
dp[i][j * 2] = Math.max(dp[i - 1][j * 2], -prices[i] + (j != 0 ? dp[i - 1][j * 2 - 1] : 0));
dp[i][j * 2 + 1] = Math.max(dp[i - 1][j * 2 + 1], dp[i - 1][j * 2] + prices[i]);
}
}
int ans = 0;
for(int i=0;i<2*k;i++){
ans = Math.max(ans,dp[n-1][i]);
}
return ans;
}
}
5 买卖股票含冷冻期
5.1 递归关系
与第二题一样,无限制交易次数,但是交易一次后一天内不能再次交易
设置二维数据dp,dp[i][0]代表第i天手上有股票的最大收益,可能是之前买的,也可能是今天买的,dp[i][1]代表第i天手上无股票的最大收益,可能本来就没有买,可能是卖了。
那么,当前日有股票的最大收益两种情况:
昨天也有股票的最大收益
今日入股的成本,加上前天手上无股票的收益
而当前日无股票的最大收益:
- 昨天也无股票的最大收益
- 今日卖出,加上昨天手上有股票的收益
-
当日有股票的时候
d p [ i ] [ 0 ] = m a x ( d p [ i − 1 ] [ 0 ] , d p [ i − 2 ] [ 1 ] − p r i c e [ i ] ) dp[i][0] = max(dp[i-1][0],dp[i-2][1]-price[i]) dp[i][0]=max(dp[i−1][0],dp[i−2][1]−price[i]) -
当日无股票的时候
d p [ i ] [ 1 ] = m a x ( d p [ i − 1 ] [ 1 ] , d p [ i − 1 ] [ 0 ] + p r i c e [ i ] ) dp[i][1] = max(dp[i-1][1],dp[i-1][0]+price[i]) dp[i][1]=max(dp[i−1][1],dp[i−1][0]+price[i])
2.2 边界条件
初始化两天
-
第一日有股票
d p [ 0 ] [ 0 ] = − p r i c e [ 0 ] dp[0][0] = -price[0] dp[0][0]=−price[0] -
第一日无股票
d p [ 0 ] [ 1 ] = 0 dp[0][1] = 0 dp[0][1]=0 -
第二日有股票
d p [ 1 ] [ 0 ] = m a x ( d p [ 0 ] [ 0 ] , d p [ 0 ] [ 1 ] − p r i c e [ 1 ] ) dp[1][0] = max(dp[0][0],dp[0][1]-price[1]) dp[1][0]=max(dp[0][0],dp[0][1]−price[1]) -
第二日无股票
d p [ 1 ] [ 1 ] = m a x ( d p [ 0 ] [ 1 ] , d p [ 0 ] [ 0 ] + p r i c e [ 1 ] ) dp[1][1] = max(dp[0][1],dp[0][0]+price[1]) dp[1][1]=max(dp[0][1],dp[0][0]+price[1])
2.3 代码
class Solution {
public int maxProfit(int[] prices) {
if(prices==null||prices.length==0){
return 0;
}
int len = prices.length;
if(len<2){
return 0;
}
int[][]dp = new int[len][2];
//初始化
dp[0][0] = -prices[0];
dp[0][1] = 0;
dp[1][0] = Math.max(dp[0][0],dp[0][1]-prices[1]);
dp[1][1] = Math.max(dp[0][1],dp[0][0]+prices[1]);
//开始遍历
for(int i = 2;i<len;i++){
dp[i][0] = Math.max(dp[i-1][0],dp[i-2][1]-prices[i]);
dp[i][1] = Math.max(dp[i-1][1],dp[i-1][0]+prices[i]);
}
return Math.max(dp[len-1][0],dp[len-1][1]);
}
}
6 买卖股票含手续费
5.1 递归关系
与第二题一样,无限制交易次数,交易一次后要扣钱fee
设置二维数据dp,dp[i][0]代表第i天手上有股票的最大收益,可能是之前买的,也可能是今天买的,dp[i][1]代表第i天手上无股票的最大收益,可能本来就没有买,可能是卖了。
那么,当前日有股票的最大收益两种情况:
昨天也有股票的最大收益
今日入股的成本,加上昨天手上无股票的收益
而当前日无股票的最大收益:
- 昨天也无股票的最大收益
- 今日卖出,加上昨天手上有股票的收益,并且减去手续费fee
- 当日有股票的时候
d p [ i ] [ 0 ] = m a x ( d p [ i − 1 ] [ 0 ] , d p [ i − 1 ] [ 1 ] − p r i c e [ i ] ) dp[i][0] = max(dp[i-1][0],dp[i-1][1]-price[i]) dp[i][0]=max(dp[i−1][0],dp[i−1][1]−price[i])
- 当日无股票的时候
d p [ i ] [ 1 ] = m a x ( d p [ i − 1 ] [ 1 ] , d p [ i − 1 ] [ 0 ] + p r i c e [ i ] − f e e ) dp[i][1] = max(dp[i-1][1],dp[i-1][0]+price[i]-fee) dp[i][1]=max(dp[i−1][1],dp[i−1][0]+price[i]−fee)
2.2 边界条件
初始化两天
-
第一日有股票
d p [ 0 ] [ 0 ] = − p r i c e [ 0 ] dp[0][0] = -price[0] dp[0][0]=−price[0] -
第一日无股票
d p [ 0 ] [ 1 ] = 0 dp[0][1] = 0 dp[0][1]=0
2.3 代码
class Solution {
public int maxProfit(int[] prices, int fee) {
if(prices==null||prices.length==0){
return 0;
}
int len = prices.length;
if(len<2){
return 0;
}
int[][]dp = new int[len][2];
//初始化
dp[0][0] = -prices[0];
dp[0][1] = 0;
//开始遍历
for(int i = 1;i<len;i++){
dp[i][0] = Math.max(dp[i-1][0],dp[i-1][1]-prices[i]);
dp[i][1] = Math.max(dp[i-1][1],dp[i-1][0]+prices[i]-fee);
}
return Math.max(dp[len-1][0],dp[len-1][1]);
}
}