参考:传送门
定义通用状态dp[i][j][k] 数组,含义dp[第一天...第N天的股票价格 ][限制交易次数][ 0未持有股票;1持有股票 ]
通用状态转移方程:
dp[i][j][0] = Math.max(dp[i - 1][ j ][0], dp[i - 1][ j ][1] + prices[i] );
dp[i][j][1] = Math.max(dp[i - 1][ j ][1], dp[i - 1][ j-1 ][0] - prices[i] );
初始化:根据不同题目进行调整
买股票最佳时机I(点击题目进入原题)
此题目没有交易次数的限制
public int maxProfit(int[] prices) {
int len = prices.length;
if(len ==0 || len == 1) {
return 0;
}
int[][] dp = new int[ len + 2 ][4]; //dp[i][j] 表示第i天股票状态为( 1持有股票、0 没有股票 )的尽量多次数
for (int i = 0; i < len; i++) {
if (i == 0) {
dp[i][0] = 0;
dp[i][1] = - prices[i];
continue;
}else if(i == 1){
dp[1][0] = Math.max(dp[0][0],dp[0][1]+prices[i]);
dp[1][1] = Math.max(dp[0][1], - prices[i]);
continue;
}
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 - 2][0] - prices[i]);
}
return dp[len-1][0];
}
买卖股票的最佳时机含手续费(点击题目进入原题)
public int maxProfit(int[] prices, int fee) {
int len = prices.length;
if(len ==0 ) {
return 0;
}
int[][] dp = new int[ len + 2 ][4]; //dp[i][j] 表示第i天股票状态为( 1持有股票、0 没有股票 )的最大利润
for (int i = 0; i < len; i++) {
if (i == 0) {
dp[i][0] = 0;
dp[i][1] = - prices[i] ;
continue;
}
dp[i][0] = Math.max(dp[i - 1][0], dp[i - 1][1] + prices[i] - fee);
dp[i][1] = Math.max(dp[i - 1][1], dp[i - 1][0] - prices[i]);
}
return dp[len-1][0];
}
买卖股票的最佳时机 III(点击题目进入原题)
public int maxProfit(int[] prices) {
int len = prices.length;
if(len == 0 ) {
return 0;
}
int[][][] dp = new int[ len + 2 ][4][2]; // dp[i][j][k] 表示第i天股票进行j笔交易(最多两笔)并且状态为k(0无持有 1持有)的最大利润
for (int i = 0; i < len; i++) {
for (int j = 1; j <= 2; j++) {
if(i == 0 && j == 1) {
dp[i][j][0] = 0;
dp[i][j][1] = -prices[i];
continue;
}else if(i == 0 && j == 2) {
dp[i][j][0] = 0;
dp[i][j][1] = -999999;
continue;
}
dp[i][j][0] = Math.max(dp[i - 1][ j ][0], dp[i - 1][ j ][1] + prices[i] );
dp[i][j][1] = Math.max(dp[i - 1][ j ][1], dp[i - 1][ j-1 ][0] - prices[i] );
}
}
return Math.max(dp[len-1][1][0],dp[len-1][2][0]);
}
买卖股票的最佳时机 IV(点击题目进入原题)
一开始提交下面代码的时候,报超出内存的错误
public int maxProfit(int k, int[] prices) {
int len = prices.length;
if(len == 0 ) {
return 0;
}
int[][][] dp = new int[ len + 2 ][k+2][2]; // dp[i][j] 表示第i天股票进行j笔交易(最多两笔)并且状态为(0无持有 1持有)的最大利润
for (int i = 0; i < len; i++) {
for (int j = 1; j <= k; j++) {
if(i == 0 && j == 1) {
dp[i][j][0] = 0;
dp[i][j][1] = -prices[i];
continue;
}else if(i == 0 && j == 2) {
dp[i][j][0] = 0;
dp[i][j][1] = -999999;
continue;
} else if(i == 0){
dp[i][j][0] = 0;
dp[i][j][1] = -999999;
continue;
}
dp[i][j][0] = Math.max(dp[i - 1][ j ][0], dp[i - 1][ j ][1] + prices[i] );
dp[i][j][1] = Math.max(dp[i - 1][ j ][1], dp[i - 1][ j-1 ][0] - prices[i] );
}
}
int max = 0;
for(int i = 1;i <= k;i++){
max = Math.max(max,dp[len-1][i][0]);
}
return max;
}
优化
滚动数组优化空间,变成二维
int[][] dp = new int[k+2][2]; // dp[i][j] 表示进行j笔交易(最多两笔)并且状态为k(0无持有 1持有)的最大利润
for (int i = 0; i < len; i++) {
for (int j = 1; j <= k; j++) {
if(i == 0 && j == 1) {
dp[j][0] = 0;
dp[j][1] = -prices[i];
continue;
}else if(i == 0 && j == 2) {
dp[j][0] = 0;
dp[j][1] = -999999;
continue;
} else if(i == 0){
dp[j][0] = 0;
dp[j][1] = -999999;
continue;
}
dp[j][0] = Math.max(dp[ j ][0], dp[ j ][1] + prices[i] );
dp[j][1] = Math.max(dp[ j ][1], dp[ j-1 ][0] - prices[i] );
}
}
int max = 0;
for(int i = 1;i <= k;i++ ){
max = Math.max(dp[i][0],max);
}
return max;
结果还是超出内存空间。。。
结果发现k和prices[]数组的长度有关系,加入如下的判断即可
int n = prices.length;
if (k >= n / 2) { // 这种情况下该问题退化为普通的股票交易问题
int maxProfit = 0;
for (int i = 1; i < n; i++) {
if (prices[i] > prices[i - 1]) {
maxProfit += prices[i] - prices[i - 1];
}
}
return maxProfit;
}