所用代码 java
买卖股票3,最多只能买卖2次。
dp[i]
递推公式:
dp[i][0] = dp[i-1][0]
第i次不操作,由前一次不操作得出 (也就是0)dp[i][1] = max(dp[i-1][1], dp[i-1][0] - peice[i])
第一次持有 = i-1天持有 或 第一次买入(i-1天不操作)dp[i][2] = max(dp[i-1][2], dp[i-1][1] + price[i])
第一次不持有 = i-1天不持有 或 i-1天的时候卖出dp[i][3] = max(dp[i-1][3], dp[i-1][2] - peice[i])
第二次持有 = i-1天持有 或 i-1天买入dp[i][4] = max(dp[i-1][4], dp[i-1][3] + price[i])
第二次不持有 = i-1天卖出 或 i-1天卖出初始化:
dp[0][0] = 0
dp[0][1] = -price[i]
dp[0][2] = 0
第一次买入之后又卖出dp[0][3] = -price[i]
第一次买入又卖出,第二次买入dp[0][4] = 0
第一次买入又卖出,然后第二次买入又卖出遍历顺序:1 <= i < price.length
打印dp
结果
class Solution {
public int maxProfit(int[] prices) {
int[][] dp = new int[prices.length][4];
// 初始化
dp[0][0] = -prices[0]; // 第一次买入
dp[0][1] = 0; // 第一次卖出
dp[0][2] = -prices[0]; // 第二次买入
dp[0][3] = 0; // 第二次卖出
for (int i = 1; i < prices.length; 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]);
// System.out.printf("dp[%d][0]=%d dp[%d][1]=%d dp[%d][2]=%d dp[%d][3]=%d\n",i,dp[i][0],i,dp[i][1],i,dp[i][2],i,dp[i][3]);
}
return dp[prices.length-1][3];
}
}
打印结果:
Your input:[3,3,5,0,0,3,1,4]
Output:6
Expected:6
stdout: 从第二天开始
第一次买入 第一次卖出 第二次买入 第二次卖出
dp[1][0]=-3 dp[1][1]=0 dp[1][2]=-3 dp[1][3]=0
dp[2][0]=-3 dp[2][1]=2 dp[2][2]=-3 dp[2][3]=2
dp[3][0]=0 dp[3][1]=2 dp[3][2]=2 dp[3][3]=2
dp[4][0]=0 dp[4][1]=2 dp[4][2]=2 dp[4][3]=2
dp[5][0]=0 dp[5][1]=3 dp[5][2]=2 dp[5][3]=5
dp[6][0]=0 dp[6][1]=3 dp[6][2]=2 dp[6][3]=5
dp[7][0]=0 dp[7][1]=4 dp[7][2]=2 dp[7][3]=6
可以观察到第i的状态都是由i-1天的状态推导而出的,所以我们可以只用一个一维的dp数组来表示
class Solution {
public int maxProfit(int[] prices) {
int[] dp = new int[4];
// 初始化
dp[0] = -prices[0]; // 第一次买入
dp[1] = 0; // 第一次卖出
dp[2] = -prices[0]; // 第二次买入
dp[3] = 0; // 第二次卖出
for (int i = 1; i < prices.length; i++) {
dp[0] = Math.max(dp[0], -prices[i]);
dp[1] = Math.max(dp[1], dp[0]+prices[i]);
dp[2] = Math.max(dp[2], dp[1]-prices[i]);
dp[3] = Math.max(dp[3], dp[2]+prices[i]);
// System.out.printf("dp[0]=%d dp[1]=%d dp[2]=%d dp[3]=%d\n",dp[0],dp[1],dp[2],dp[3]);
}
return dp[3];
}
}
打印dp:
Your input:[3,3,5,0,0,3,1,4]
Output:6
Expected:6
stdout:
dp[0]=-3 dp[1]=0 dp[2]=-3 dp[3]=0
dp[0]=-3 dp[1]=2 dp[2]=-3 dp[3]=2
dp[0]=0 dp[1]=2 dp[2]=2 dp[3]=2
dp[0]=0 dp[1]=2 dp[2]=2 dp[3]=2
dp[0]=0 dp[1]=3 dp[2]=2 dp[3]=5
dp[0]=0 dp[1]=3 dp[2]=2 dp[3]=5
dp[0]=0 dp[1]=4 dp[2]=2 dp[3]=6
从买卖股票3推断出dp[i]的状态在奇数次为买入,偶数次为卖出。
dp[i] [j]:i 第i支股票,j 买入或者卖出(是否持有)所获得的最大利润
递推公式:从买卖股票3可以推导出
dp[0][0]= 0
0位表示无状态(可省略)
dp[i][j+1] = max(dp[i-1][j+1], dp[i-1][j] - price[i])
dp[i][j+2] = max(dp[i-1][j+2], dp[i-1][j+1] + price[i])
初始化:同理
dp[0][j+1] = -price[i]
奇数次为买入dp[0][j+2] = 0]
偶数次卖出,默认初始化为0遍历顺序: 1<=i
打印dp
class Solution {
public int maxProfit(int k, int[] prices) {
int[][] dp = new int[prices.length][2*k + 1];
// 初始化
// 奇数次表示持有(买入),n次买入表示前面买卖钱花完了,所以都是prices[0]
// j+2控制每一次为奇数 偶数次控制不持有(卖出),默认为0
for (int j = 1; j < 2*k; j+=2) {
dp[0][j] = -prices[0];
}
for (int i = 1; i < prices.length; i++) {
// j+=2,一组一组的表示每一次持有或者不持有的状态
for (int j = 0; j < 2*k; j+=2) { // j最多能到2k-2,然后加2等于2k(即最后一位)
// 持有:前一天持有 或者 前一天持有j+1-1 加买入-prices[i]
dp[i][j+1] = Math.max(dp[i-1][j+1], dp[i-1][j] - prices[i]);
// 不持有:前一天不持有 或者 前一天不持有j+2-1 加卖出+prices[i]
dp[i][j+2] = Math.max(dp[i-1][j+2], dp[i-1][j+1] + prices[i]);
}
}
return dp[prices.length-1][2*k];
}
}
打印dp:
Your input:2
[3,2,6,5,0,3]
Output:7
Expected:7
stdout:
第1次持有 第1次不持有 第2次持有 第2次不持有
dp[1][1]=-2 dp[1][2]=0 dp[1][3]=-2 dp[1][4]=0
dp[2][1]=-2 dp[2][2]=4 dp[2][3]=-2 dp[2][4]=4
dp[3][1]=-2 dp[3][2]=4 dp[3][3]=-1 dp[3][4]=4
dp[4][1]=0 dp[4][2]=4 dp[4][3]=4 dp[4][4]=4
dp[5][1]=0 dp[5][2]=4 dp[5][3]=4 dp[5][4]=7
买卖股票4是在买卖股票3的基础上,通过找规律得出的结果,相当于一个通式。
从地推公式第i天的状态完全是由i-1天的状态推导而出,所以我们可以写出一维数组的形式:
class Solution {
public int maxProfit(int k, int[] prices) {
int[] dp = new int[2*k + 1];
// 初始化
// 奇数次表示持有(买入),n次买入表示前面买卖钱花完了,所以都是prices[0]
// j+2控制每一次为奇数 偶数次控制不持有(卖出),默认为0
for (int j = 1; j < 2*k; j+=2) {
dp[j] = -prices[0];
}
for (int i = 1; i < prices.length; i++) {
// j+=2,一组一组的表示每一次持有或者不持有的状态
for (int j = 0; j < 2*k; j+=2) {
// 持有:前一天持有 或者 前一天持有j+1-1 加买入-prices[i]
dp[j+1] = Math.max(dp[j+1], dp[j] - prices[i]);
// 不持有:前一天不持有 或者 前一天不持有j+2-1 加卖出+prices[i]
dp[j+2] = Math.max(dp[j+2], dp[j+1] + prices[i]);
// System.out.printf("dp[%d]=%d\t",j+1,dp[j+1]);
// System.out.printf("dp[%d]=%d\t",j+2,dp[j+2]);
}
// System.out.println();
}
return dp[2*k];
}
}
打印dp:
Your input:2
[3,2,6,5,0,3]
Output:7
Expected:7
stdout:
第1次持有 第1次不持有 第2次持有 第2次不持有
dp[1]=-2 dp[2]=0 dp[3]=-2 dp[4]=0
dp[1]=-2 dp[2]=4 dp[3]=-2 dp[4]=4
dp[1]=-2 dp[2]=4 dp[3]=-1 dp[4]=4
dp[1]=0 dp[2]=4 dp[3]=4 dp[4]=4
dp[1]=0 dp[2]=4 dp[3]=4 dp[4]=7