1、交易次數限制為一次的情況:
題目:
給定一個數組,它的第 i 個元素是一支給定股票第 i 天的價格。
如果你最多只允許完成一筆交易(即買入和賣出一支股票一次),設計一個算法來計算你所能獲取的最大利潤。
注意:你不能在買入股票前賣出股票
分析:
由於交易的次數被限制為一次,所以我們要找的就是在這幾天當中股票價格的最低值以及這之后的股票價格的最高值,相減就是最大利潤。
方法一:
貪心思想,找出當前這個日子之前的股票最低價格買入,然后判斷以當前股票價格賣出是否是截止到目前為止的最大值。貪心思想在做出選擇時,就是直接做出當前問題中
看起來的最優解。
實現代碼如下:
public int maxProfit(int[] prices) { if (prices==null || prices.length < 2) return 0; int min_price = prices[0]; int max= 0; for (int price : prices) { if (min_price > price) min_price = price; else { max = Math.max(price-min_price,max); } } return max; }
方法二:
動態規划,
1.定義一個狀態轉移數組dp,表示當前的獲得的最大利潤,dp[i][0]表示在第i天,沒有持有股票的最大利潤;dp[i][1]表示在第i天,持有股票時的最大利潤。
2.狀態轉移方程:
dp[i][0] = max{dp[i-1][0],dp[i-1][1]+price[i-1]} 表示當前沒有持有可能是前一天也沒有持有股票,也可能是前一天持有股票,今天出售了
dp[i][1] = max{dp[i-1][1],-price[ii-1]} 表示當前持有股票的原因可能是前一天也持有了股票而今天沒有賣,也可能是前一天沒有持有股票,今天購買了
3.dp[1][0] = 0,dp[1][1] = -price[0]
通過觀察狀態方程可以發現,第i天的利潤只和第i-1天有關,因此可以將這個維度去掉,將狀態數組替換為狀態轉移變量,從而減少空間復雜度。
public int maxProfit2(int[] prices) { if (prices==null || prices.length < 2) return 0; int dp_i_0 = 0; int dp_i_1 = -prices[0]; for (int i = 1; i < prices.length; i++) { dp_i_0 = Math.max(dp_i_0,dp_i_1+prices[i]); dp_i_1 = Math.max(dp_i_1,-prices[i]); } return dp_i_0; }
2、交易次數限制為一次的情況:
題目:
給定一個數組,它的第 i 個元素是一支給定股票第 i 天的價格。
設計一個算法來計算你所能獲取的最大利潤。你可以盡可能地完成更多的交易(多次買賣一支股票)。
注意:你不能同時參與多筆交易(你必須在再次購買前出售掉之前的股票)。
分析:
方法一:
貪心思想,交易次數是無限制的,從最理想的情況來看是從第一天開始到最后一天股票價格都是一直增長的,那么此時所有的price[i] > price[i-1]都成立,所以最大利潤就是
price[n-1] - price[0];但是在實際的情況中,股票價格肯定是有波動的,也就是存在着price[i] < price[i]的情況,那么我們依據貪心思想,考慮在當前的第i天,如果price[i] > price[i-1]成立,那么就是第i-1天買入,第i天賣出,股價之差就算入總利潤,最大的利潤就是所有的price[i] > price[i-1]情況相加。
public int maxProfit(int[] prices) { if (prices==null || prices.length<2) return 0; int profit = 0; for (int i = 1; i < prices.length; i++) { if (prices[i] > prices[i-1]) profit += (prices[i]-prices[i-1]); } return profit; }
方法二:
動態規划,由於交易次數是無限的,所以我們可以不明確表示交易次數這個狀態,定義一個狀態轉移數組表示當前的獲得的最大利潤,dp[i][0]表示在第i天,沒有持有股票的最大利潤;dp[i][1]表示在第i天,持有股票時的最大利潤。
確定狀態轉移方程:
dp[i][0] = max{dp[i-1][0],dp[i-1][1]+price[i-1]} 表示當前沒有持有可能是前一天也沒有持有股票,也可能是前一天持有股票,今天出售了
dp[i][1] = max{dp[i-1][1],dp[i-1][0]-price[i-1]} 表示當前持有股票的原因可能是前一天也持有了股票而今天沒有賣,也可能是前一天沒有持有股票,今天購買了
此處與交易次數限制為1次的時候的最大區別是我們在考慮dp[i][1]的值的時候,選擇范圍是當前一天也持有了股票而今天沒有賣,或者是前一天沒有持有股票,今天購買了。而今天購買了,那么總體的利潤就變成了前一天沒有持有的股票的時候擁有的最大利潤減去今天購買的股票價格,也就是說因為交易次數是無限的,所以前一天所擁有的利潤可能不為0,所以也就需要明確考慮,而交易次數限制為1次的時候,假如第i天要購買股票,那么第i天之前是沒有發生交易的,所以也就沒有利潤,利潤值為0,這是最大的區別。
public int maxProfit2(int[] prices) { int n = prices.length; if (prices==null || n<2) return 0; int[][] dp = new int[n + 1][2]; dp[1][0] = 0; dp[1][1] = -prices[0]; for (int i = 2; i <= n; i++) { dp[i][0] = Math.max(dp[i-1][0],dp[i-1][1]+prices[i-1]); dp[i][1] = Math.max(dp[i-1][1],dp[i-1][0]-prices[i-1]); } return dp[n][0]; }