import java.util.*;
/**
* NC167 买卖股票的最好时机(四)
* @author d3y1
*/
public class Solution {
/**
* 代码中的类名、方法名、参数名已经指定,请勿修改,直接返回方法规定的值即可
*
*
* @param prices int整型一维数组
* @param k int整型
* @return int整型
*/
public int maxProfit (int[] prices, int k) {
// return solution1(prices, k);
return solution2(prices, k);
// return solution3(prices, k);
// return solution4(prices, k);
// return solution5(prices, k);
}
/**
* 动态规划: 状态机
*
* dp[i][1]表示股票前i天价格第一次交易买入后所得收益
* dp[i][2]表示股票前i天价格第一次交易卖出后所得收益
* dp[i][3]表示股票前i天价格第二次交易买入后所得收益
* dp[i][4]表示股票前i天价格第二次交易卖出后所得收益
* ...
* dp[i][2k-1]表示股票前i天价格第k次交易卖出后所得收益
* dp[i][2k] 表示股票前i天价格第k次交易卖出后所得收益
*
* 2k种状态:
* 第一次交易买入: 要么是之前买的, 要么是今天买的
* 第一次交易卖出: 要么是之前卖的, 要么是今天卖的
* 第二次交易买入: 要么是之前买的, 要么是今天买的
* 第二次交易卖出: 要么是之前卖的, 要么是今天卖的
* ...
* 第k次交易买入: 要么是之前买的, 要么是今天买的
* 第k次交易卖出: 要么是之前卖的, 要么是今天卖的
*
* 关系式:
* { Math.max(dp[i-1][j], dp[i-1][j-1]-prices[i-1]) , j%2 == 1
* dp[i][j] = {
* { Math.max(dp[i-1][j], dp[i-1][j-1]+prices[i-1]) , j%2 == 0
*
* @param prices
* @param k
* @return
*/
private int solution1(int[] prices, int k){
int n = prices.length;
k = Math.min(k, n/2);
int[][] dp = new int[n+1][2*k+1];
// init
for(int j=1; j<2*k+1; j++){
if(j%2 == 1){
dp[1][j] = -prices[0];
}else{
dp[1][j] = 0;
}
}
for(int i=2; i<=n; i++){
for(int j=1; j<2*k+1; j++){
if(j%2 == 1){
dp[i][j] = Math.max(dp[i-1][j], dp[i-1][j-1]-prices[i-1]);
}else{
dp[i][j] = Math.max(dp[i-1][j], dp[i-1][j-1]+prices[i-1]);
}
}
}
return dp[n][2*k];
}
/**
* 动态规划: 三维数组
*
* dp[i][j][0]表示前i天股票价格交易j次(一买一卖)后手上未持股票时的最大收益
* dp[i][j][1]表示前i天股票价格交易j次(一买一卖)后手上持有股票时的最大收益
*
* dp[i][j][0] = Math.max(dp[i-1][j][0], dp[i-1][j][1]+prices[i]) , 1<=i<n && 1<=j<=k
* dp[i][j][1] = Math.max(dp[i-1][j][1], dp[i-1][j-1][0]-prices[i]) , 1<=i<n && 1<=j<=k
*
* @param prices
* @param k
* @return
*/
private int solution2(int[] prices, int k){
int n = prices.length;
k = Math.min(k, n/2);
int[][][] dp = new int[n][k+1][2];
// init
for(int j=1; j<=k; j++){
dp[0][j][0] = 0;
dp[0][j][1] = -prices[0];
}
for(int i=1; i<n; i++){
for(int j=1; j<=k; j++){
// dp[i-1][j][1]+prices[i] -> 卖(次数不变, j -> j)
dp[i][j][0] = Math.max(dp[i-1][j][0], dp[i-1][j][1]+prices[i]);
// dp[i-1][j-1][0]-prices[i] -> 买(次数加1, j-1 -> j)
dp[i][j][1] = Math.max(dp[i-1][j][1], dp[i-1][j-1][0]-prices[i]);
}
}
return dp[n-1][k][0];
}
/**
* 动态规划: 二维数组
*
* sell[i][j]表示前i天股票价格交易j次(一买一卖)后手上未持股票时的最大收益
* buy[i][j]表示前i天股票价格交易j次(一买一卖)后手上持有股票时的最大收益
*
* sell[i][j] = Math.max(sell[i-1][j], buy[i-1][j]+prices[i]) , 1<=i<n && 1<=j<=k
* buy[i][j] = Math.max(buy[i-1][j], sell[i-1][j-1]-prices[i]) , 1<=i<n && 1<=j<=k
*
* @param prices
* @param k
* @return
*/
private int solution3(int[] prices, int k){
int n = prices.length;
k = Math.min(k, n/2);
int[][] sell = new int[n][k+1];
int[][] buy = new int[n][k+1];
// init
for(int j=1; j<=k; j++){
sell[0][j] = 0;
buy[0][j] = -prices[0];
}
for(int i=1; i<n; i++){
for(int j=1; j<=k; j++){
// buy[i-1][j]+prices[i] -> 卖(次数不变, j -> j)
sell[i][j] = Math.max(sell[i-1][j], buy[i-1][j]+prices[i]);
// sell[i-1][j-1]-prices[i] -> 买(次数加1, j-1 -> j)
buy[i][j] = Math.max(buy[i-1][j], sell[i-1][j-1]-prices[i]);
}
}
return sell[n-1][k];
}
/**
* 动态规划: 一维数组(空间压缩)
*
* sell[j]表示交易j次(一买一卖)后手上未持股票时的最大收益
* buy[j]表示交易j次(一买一卖)后手上持有股票时的最大收益
*
* sell[j] = Math.max(sell[j], buy[j]+prices[i]) , 1<=i<n && 1<=j<=k
* buy[j] = Math.max(buy[j], sell[j-1]-prices[i]) , 1<=i<n && 1<=j<=k
*
* @param prices
* @param k
* @return
*/
private int solution4(int[] prices, int k){
int n = prices.length;
k = Math.min(k, n/2);
int[] buy = new int[k+1];
int[] sell = new int[k+1];
// init
for(int j=1; j<=k; j++){
sell[j] = 0;
buy[j] = -prices[0];
}
for(int i=1; i<n; i++){
for(int j=1; j<=k; j++){
sell[j] = Math.max(sell[j], buy[j]+prices[i]);
// 此处 sell[j-1] = sell[i][j-1](实际需要的sell[i-1][j-1]已被覆盖, 但是不影响最终结果)
// sell[i][j] = Math.max(sell[i-1][j], buy[i-1][j]+prices[i])
// => sell[i][j-1] = Math.max(sell[i-1][j-1], buy[i-1][j-1]+prices[i])
// => sell[j-1] = Math.max(sell[i-1][j-1], buy[i-1][j-1]+prices[i])
// => sell[j-1]-prices[i] = Math.max(sell[i-1][j-1]-prices[i], buy[i-1][j-1]+prices[i]-prices[i])
// = Math.max(sell[i-1][j-1]-prices[i], buy[i-1][j-1])
// buy[j] = Math.max(buy[j], sell[j-1]-prices[i])
// = Math.max(buy[i-1][j], sell[j-1]-prices[i])
// = Math.max(buy[i-1][j], sell[i-1][j-1]-prices[i], buy[i-1][j-1])
// = Math.max(buy[i-1][j], sell[i-1][j-1]-prices[i]) [buy[i-1][j] >= buy[i-1][j-1]]
// 可见, 与solution3中二维数组关系式是相同的
buy[j] = Math.max(buy[j], sell[j-1]-prices[i]);
}
}
return sell[k];
}
/**
* 动态规划
*
* dp[j]表示前j天股票价格某次交易后的最大收益
*
* { Math.max(maxProfit, dp[j]-prices[j]) , 买入
* dp[j] = {
* { Math.max(maxProfit, dp[j]+prices[j]) , 卖出
*
* @param prices
* @param k
* @return
*/
private int solution5(int[] prices, int k){
int n = prices.length;
k = Math.min(k, n/2);
int[] dp = new int[n];
int maxProfit;
for(int i=1; i<=k; i++){
// 买入
maxProfit = Integer.MIN_VALUE;
for(int j=0; j<n; j++){
dp[j] = Math.max(maxProfit, dp[j]-prices[j]);
maxProfit = Math.max(maxProfit, dp[j]);
}
// 卖出
maxProfit = Integer.MIN_VALUE;
for(int j=0; j<n; j++){
dp[j] = Math.max(maxProfit, dp[j]+prices[j]);
maxProfit = Math.max(maxProfit, dp[j]);
}
}
return dp[n-1];
}
}