每天一道LeetCode-----买卖商品问题,计算最大利润,分别有一次交易,两次交易,多次交易的情况
Best Time to Buy and Sell Stock
原題鏈接Best Time to Buy and Sell Stock
給定一個(gè)價(jià)格序列prices,其中prices[i]代表第i天商品的價(jià)格,商家需要在某一天買入,然后在之后的某一天出售,計(jì)算可以獲得的最大利潤
本質(zhì)就是計(jì)算prices[i]?prices[j]的最大值,要求i>j
假設(shè)已經(jīng)直到最后結(jié)果的最大值位置i(prices[i]),只需要減去最小值即可,那么就需要直到最小值是多少,本題中當(dāng)從左到右遍歷時(shí),完全可以隨著遍歷進(jìn)度的增加記錄最小值
代碼如下
class Solution { public:int maxProfit(vector<int>& prices) {if(prices.empty()) return 0;/* 記錄目前位置找到的最小值 */int minPrices = prices[0];/* 記錄最大差值 */int maxProfit = 0;for(int i = 1; i < prices.size(); ++i){/* 以prices[i]作為最大值,嘗試計(jì)算差值 */maxProfit = std::max(maxProfit, prices[i] - minPrices);/* 嘗試更新最小值 */minPrices = std::min(minPrices, prices[i]);}return maxProfit;} };?
當(dāng)然也可以遍歷兩次求解,第一次對于每個(gè)位置的值,計(jì)算它的右側(cè)比它大的最大值,第二次對于每個(gè)位置,計(jì)算右側(cè)的最大值與自己的差,更新最大差值,求出結(jié)果
代碼如下
class Solution { public:int maxProfit(vector<int>& prices) {int n = prices.size();vector<int> dp(n + 1, 0);/* 計(jì)算prices[i]右側(cè)的最大值,包括自己 */for(int i = n - 1; i >= 0; --i)dp[i] = std::max(dp[i + 1], prices[i]);int maxProfit = 0;/* 右側(cè)最大值 - 自身 = 最大差值 */for(int i = 0; i < n; ++i)maxProfit = std::max(maxProfit, dp[i] - prices[i]);return maxProfit;} };Best Time to Buy and Sell Stock II
原題鏈接Best Time to Buy and Sell Stock II
要求和上面一樣,不過這次可以進(jìn)行多次交易,即買,賣,買,賣,買,賣…,其中買賣不能在同一天進(jìn)行
這樣就不能像上面那樣只求一次了,其實(shí)仔細(xì)想一下,序列無非三種排列
- 遞增,假設(shè)A<B<C<D
- 遞減,假設(shè)A>B>C>D
- 無序,假設(shè)A<B>C<D
對于遞增序列,最大的差值就是D?A,因?yàn)?span id="ozvdkddzhkzd" class="MathJax_Preview">(D?A)=(D?C)+(C?B)+(B?A)>(D?C)+(B?A)
對于遞減序列,為0
對于無序序列,總可以找到若干個(gè)遞增序列,就上面的例子而言最大差值為(B?A)+(D?C),而實(shí)際上也可以寫成(D?C)+max(C?B,0)+(B?A),和遞增序列的形式是一樣的
所以只要依次計(jì)算prices[i]?prices[i?1]即可
代碼如下
class Solution { public:int maxProfit(vector<int>& prices) {int maxProfit = 0;for(int i = 1; i < prices.size(); ++i)maxProfit += std::max(prices[i] - prices[i - 1], 0);return maxProfit;} };Best Time to Buy and Sell Stock III
原題鏈接Best Time to Buy and Sell Stock III
同樣是買賣問題,本題要求最多可以完成兩次交易,計(jì)算最大利潤
利用動態(tài)規(guī)劃,對動態(tài)規(guī)劃數(shù)組states[i][j]有如下規(guī)定
- states[][0]代表0次買,0次賣的利潤
- states[][1]代表一次買,0次賣的利潤(顯然是負(fù)數(shù))
- states[][2]代表一次買,一次賣的利潤
- states[][3]代表兩次買,一次賣的利潤
- states[][4]代表兩次買,兩次賣的利潤
而states,規(guī)定它是二維數(shù)組,states[0]代表當(dāng)前的利潤,states[1]代表執(zhí)行完本次操作后的利潤,那么有
- states[1][1] = std::max(states[0][1], states[0][0] - prices[i]),表示買入商品prices[i],那么利潤就是0次買0次賣的利潤減商品價(jià)格,當(dāng)然需要和1次買0次賣比較,選擇較大的那個(gè)
- states[1][2] = std::max(states[0][2], states[0][1] + prices[i]),表示賣出商品prices[i],那么利潤就是1次買0次賣的利潤加商品價(jià)格,當(dāng)然需要和1次買1次賣比較,選擇較大的那個(gè)
- …同理
初始狀態(tài),另1次買0次賣的利潤為INT_MIN,兩次買一次賣的利潤為INT_INT,是為了讓max選擇后者,因?yàn)槌跏紶顟B(tài)沒有買入任何商品
代碼如下
class Solution { public:int maxProfit(vector<int>& prices) {vector<vector<int>> states(2, vector<int>(2 * 2 + 1, 0));states[0][0] = 0;for(int i = 1; i < 2 * 2 + 1; i += 2){states[0][i] = INT_MIN;states[0][i + 1] = 0;}int cur = 0, next = 1;for(int i = 0; i < prices.size(); ++i){states[next][1] = std::max(states[cur][1], states[cur][0] - prices[i]);states[next][2] = std::max(states[cur][2], states[cur][1] + prices[i]);states[next][3] = std::max(states[cur][3], states[cur][2] - prices[i]);states[next][4] = std::max(states[cur][4], states[cur][3] + prices[i]);/* next變?yōu)楫?dāng)前 */std::swap(next, cur);}return states[cur][2 * 2];} };Best Time to Buy and Sell Stock IV
原題鏈接Best Time to Buy and Sell Stock IV
和上面一樣,要求最多可以進(jìn)行k次交易
只需要將上面的兩次交易換成k次即可
class Solution { public:int maxProfit(int k, vector<int>& prices) {vector<vector<int>> states(2, vector<int>(2 * k + 1, 0)); states[0][0] = 0;for(int i = 1; i < 2 * k + 1; i += 2){states[0][i] = INT_MIN;states[0][i + 1] = 0;}int cur = 0, next = 1;for(int i = 1; i < prices.size(); ++i){/* 把兩次變?yōu)閗次,就需要放在循環(huán)里了 */for(int j = 1; j < 2 * k + 1; j += 2){states[next][j] = std::max(states[cur][j], states[cur][j - 1] - prices[i]);states[next][j + 1] = std::max(states[cur][j + 1], states[cur][j] + prices[i]);}std::swap(next, cur);}return states[cur][2 * k];} };不過這樣可能內(nèi)存溢出,因?yàn)槿绻鹝很大,申請的內(nèi)存很容易溢出,解決方法是當(dāng)k很大時(shí)采用其他方法。什么時(shí)候k算大呢,就是k次交易可以涉及到每一天,也就是k >= prices.size() / 2,此時(shí)就可以轉(zhuǎn)換為沒有交易次數(shù)限制的問題
代碼如下
class Solution { public:int maxProfit(int k, vector<int>& prices) {/* 如果k很大,采用其他方式 */if(k >= prices.size() / 2)return quickSolver(prices);vector<vector<int>> states(2, vector<int>(2 * k + 1, 0)); for(int i = 1; i < 2 * k + 1; i += 2){states[0][i] = INT_MIN;states[0][i + 1] = 0;}int cur = 0, next = 1;for(int i = 0; i < prices.size(); ++i){/* 把兩次變?yōu)閗次,就需要放在循環(huán)里了 */for(int j = 1; j < 2 * k + 1; j += 2){states[next][j] = std::max(states[cur][j], states[cur][j - 1] - prices[i]);states[next][j + 1] = std::max(states[cur][j + 1], states[cur][j] + prices[i]);}std::swap(next, cur);}return states[cur][2 * k];} private:/* 沒有交易次數(shù)限制,直接遍歷,見第二題 */int quickSolver(vector<int>& prices){int profit = 0;for(int i = 1; i < prices.size(); ++i)profit += std::max(prices[i] - prices[i - 1], 0);return profit;} };第一道題的第一種解法比較好,直接遍歷一遍同時(shí)記錄最小值和最大差值,同時(shí)也沒有額外的空間使用
第二道題不太容易理解,其實(shí)對于序列無非遞增,遞減,無序三種可能,分別觀察一下即可
第三第四題主要以動態(tài)規(guī)劃為主
總結(jié)
以上是生活随笔為你收集整理的每天一道LeetCode-----买卖商品问题,计算最大利润,分别有一次交易,两次交易,多次交易的情况的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: Redis源码剖析(九)对象系统概述
- 下一篇: 每天一道LeetCode-----有效回