目錄
總的狀態轉移方程:
121. 買賣股票的最佳時機(交易一次)
- 只能有一次買入,一次買出。求出最大的利潤是多少。
- 我起初的思路是,用動態規劃的方式。F(i)表示含第i天能獲取的最大收益。
F(i) = max { F(i-1) , 第i天的股票價格 - 第i天以前的最低股票價格 }
class Solution {
public int maxProfit(int[] prices) {
//動態規劃解題,設每個序號的股價爲p(i)
//設第包括序號i之前的最大收益爲F(i),min表示i序號之前的最小值
//F(i) = max{F(i-1), p(i)-min}
if(prices.length < 2){
return 0;
}
int min = Math.min(prices[0], prices[1]);
int f = prices[1] - prices[0];
for(int i = 2; i<prices.length; i++){
min = prices[i] < min ? prices[i] : min;
f = Math.max(f, prices[i] - min);
}
return f > 0 ? f : 0;
}
}
- 套用總的狀態轉移方程:
public class Solution {
public int maxProfit(int[] prices) {
int dp0 = 0;
int dp1 = Integer.MIN_VALUE;
for (int i = 0; i < prices.length; i++) {
// 手中沒有股票 可能昨天就沒有;昨天有,今天拋售了。
dp0 = Math.max(dp0, dp1 + prices[i]);
// 手中有股票 可能昨天沒有,今天剛買的(因爲只讓買一次,所以新買的話,dp爲負的股票價格);昨天就有
dp1 = Math.max(dp1, - prices[i]);
}
return Math.max(dp0, dp1);
}
}
122. 買賣股票的最佳時機 II(交易無窮次)
直接套用普適性的狀態轉移方程:發現和第一道題目只有一點點不同。
public class Demo02 {
public int maxProfit(int[] prices) {
int dp0 = 0;
int dp1 = Integer.MIN_VALUE;
for (int i = 0; i < prices.length; i++) {
int tmp = dp0;
// 手中沒有股票 可能昨天就沒有;昨天有,今天拋售了。
dp0 = Math.max(dp0, dp1 + prices[i]);
// 手中有股票 可能昨天沒有,今天剛買的(因爲只讓買一次,所以新買的話,dp爲負的股票價格);昨天就有
dp1 = Math.max(dp1, tmp - prices[i]);
}
return Math.max(dp0, dp1);
}
}
123. 買賣股票的最佳時機 III(交易兩次)
public class Solution {
public int maxProfit(int[] prices) {
int dp_2_0 = 0;// 第二次交易,手中沒有股票
int dp_2_1 = Integer.MIN_VALUE;// 第二次交易,手中有股票
int dp_1_0 = 0;// 第一次交易,手中沒有股票
int dp_1_1 = Integer.MIN_VALUE;// 第一次交易,手中有股票
for (int price : prices) {
// 沒有股票,可能昨天就沒有,或者今天賣了
dp_2_0 = Math.max(dp_2_0, dp_2_1 + price);
dp_1_0 = Math.max(dp_1_0, dp_1_1 + price);
// 持有股票。可能昨天就持有。或者今天新買的
dp_2_1 = Math.max(dp_2_1, dp_1_0 - price);
dp_1_1 = Math.max(dp_1_1, -price);
}
return dp_2_0;
}
}
188. 買賣股票的最佳時機 IV(k次交易)
需要對k進行一下判斷,否則會超出內存限制。
public class Solution {
public int maxProfit(int k, int[] prices) {
// 這種情況下,相當於交易次數爲無窮次的情況。
if (k > prices.length / 2) {
int dp0 = 0;
int dp1 = Integer.MIN_VALUE;
for (int i = 0; i < prices.length; i++) {
int tmp = dp0;
// 手中沒有股票 可能昨天就沒有;昨天有,今天拋售了。
dp0 = Math.max(dp0, dp1 + prices[i]);
// 手中有股票 可能昨天沒有,今天剛買的(因爲只讓買一次,所以新買的話,dp爲負的股票價格);昨天就有
dp1 = Math.max(dp1, tmp - prices[i]);
}
return Math.max(dp0, dp1);
}
// k<= len/2
int[] dp_k_1 = new int[k + 1]; // k次交易,手中有股票
int[] dp_k_0 = new int[k + 1]; // k次交易,手中沒有股票
Arrays.fill(dp_k_1, Integer.MIN_VALUE);
for (int price : prices) {
for (int i = 1; i <= k; i++) {
// 第i次交易,手上沒有股票,
dp_k_0[i] = Math.max(dp_k_0[i], dp_k_1[i] + price);
dp_k_1[i] = Math.max(dp_k_1[i], dp_k_0[i - 1] - price);
}
}
Arrays.sort(dp_k_0);
return dp_k_0[k];
}
}
309. 最佳買賣股票時機含冷凍期
- 可以發現,和沒有冷凍期相比。就是當買入股票的時候,需要是在前兩天沒買股票的基礎上進行。
public class Solution {
public int maxProfit(int[] prices) {
// 含有冷凍期,可以交易無數次。
// 1. 手中可以有股票,可以沒股票
// 2. 天數變量
int dp_0 = 0;
int dp_1 = Integer.MIN_VALUE;
int tmp = 0;// 代表dp_0 i-2那天的。
for (int i = 0; i < prices.length; i++) {
int x = dp_0;// 昨天的。
dp_0 = Math.max(dp_0, dp_1 + prices[i]);
dp_1 = Math.max(dp_1, tmp - prices[i]);
tmp = x;// 明天來看的話,tmp就是i-2天的。
}
return dp_0;
}
}
714. 買賣股票的最佳時機含手續費
public class Solution {
public int maxProfit(int[] prices, int fee) {
// 有手續費用。我們假定買入的時候需要支付手續費。
int dp_0 = 0;
int dp_1 = Integer.MIN_VALUE;
for (int price : prices) {
dp_0 = Math.max(dp_0, dp_1 + price);
dp_1 = Math.max(dp_1, dp_0 - price - fee);
}
return dp_0;
}
}