LintCode 買賣股票的最佳時機I II III IV
今天我做了LintCode上的買賣股票系列題目。總共4道題目,主要使用了動態規劃的方法,在此寫出我的程式碼和思路以便交流和回顧。
1.假設有一個數組,它的第i個元素是一支給定的股票在第i天的價格。如果你最多隻允許完成一次交易(例如,一次買賣股票),設計一個演算法來找出最大利潤。
樣例:給出一個數組樣例 [3,2,3,1,2], 返回 1
思路:遍歷陣列,在能獲利的地方嘗試賣出,並比較賣出利潤是否最大。在成本更低的地方則買入。
class Solution { public: /** * @param prices: Given an integer array * @return: Maximum profit */ int maxProfit(vector<int> &prices) { // write your code here if(prices.size() == 0){ //時刻注意陣列越界 return 0; } int max = 0; int cur = prices[0]; for(int i = 0; i < prices.size(); ++i){ if(prices[i] < cur){ //賣掉會虧 cur = prices[i]; }else{ //賣掉能掙 int tmp = prices[i] - cur; if(tmp > max){ max = tmp; } } } return max; } };
2.假設有一個數組,它的第i個元素是一個給定的股票在第i天的價格。設計一個演算法來找到最大的利潤。你可以完成儘可能多的交易(多次買賣股票)。然而,你不能同時參與多個交易(你必須在再次購買前出售股票)。
樣例:給出一個數組樣例[2,1,2,0,1], 返回 2
思路:由於買賣次數無限,所以只要能獲利就進行買賣,這樣能保證所有利潤都吃到自然利潤最大。
class Solution { public: /** * @param prices: Given an integer array * @return: Maximum profit */ int maxProfit(vector<int> &prices) { // write your code here int i, d; int max = 0; for(i = 1; i < prices.size(); ++i){//只要有錢賺就買賣 d = prices[i] - prices[i - 1]; if(d > 0){ max += d; } } return max; } };
3.假設你有一個數組,它的第i個元素是一支給定的股票在第i天的價格。設計一個演算法來找到最大的利潤。你最多可以完成兩筆交易。
樣例:給出一個樣例陣列 [4,4,6,1,1,4,2,5], 返回 6
思路:使用遞推的方法,先計算第一次買賣的最大利潤區間,第二次買賣是要想獲得最大利潤,要麼是在第一次買賣的區間之前再進行一次最大利潤買賣,要麼是在第一次買賣的區間之後再進行一次最大利潤買賣,要麼是從第一次買賣的區間中切除一個最大虧損區間,將其切分成兩次買賣。比較三種方案哪種帶來的利潤最多就可以得到兩筆交易後的最大利潤。計算最大利潤區間使用題1的演算法,計算最大虧損區間只要改變一下題1演算法的符號。
struct Sub{ //買賣區間
int begin;
int end;
bool fill;
Sub(int begin = 0, int end = 0, bool fill = true){
this->begin = begin;
this->end = end;
this->fill = fill;
}
};
class Solution {
public:
/**
* @param prices: Given an integer array
* @return: Maximum profit
*/
int maxProfit(vector<int> &prices) {
if(prices.size() == 0){
return 0;
}
Sub tmpSub;
//第一次買賣後的最大利潤
int max = findMaxSub(prices, 0, prices.size() - 1, tmpSub);
//第二次買賣後的最大利潤
vector<Sub> subArr;
subArr.push_back(tmpSub);
subArr.push_back(Sub(0, tmpSub.begin - 1, false));
subArr.push_back(Sub(tmpSub.end + 1, prices.size() - 1, false));
int maxSub = 0;
for(int i = 0; i < subArr.size(); ++i){
int tmp;
if(subArr[i].fill){
tmp = -findMinSub(prices, subArr[i].begin, subArr[i].end, tmpSub);
}else{
tmp = findMaxSub(prices, subArr[i].begin, subArr[i].end, tmpSub);
}
if(tmp > maxSub){
maxSub = tmp;
}
}
return max + maxSub;
}
int findMaxSub(vector<int> &arr, int begin, int end, Sub& sub) {
if(begin >= end)
return 0;
sub.fill = true;
int r = 0;
int curIndex = begin;
for(int i = begin; i <= end; ++i){
if(arr[i] < arr[curIndex]){
curIndex = i;
}else{
int tmp = arr[i] - arr[curIndex];
if(tmp > r){
r = tmp;
sub.end = i;
sub.begin = curIndex;
}
}
}
return r;
}
int findMinSub(vector<int> &arr, int begin, int end, Sub& sub) {
if(begin >= end)
return 0;
sub.fill = false;
int r = 0;
int curIndex = begin;
for(int i = begin; i <= end; ++i){
if(arr[i] > arr[curIndex]){
curIndex = i;
}else{
int tmp = arr[i] - arr[curIndex];
if(tmp < r){
r = tmp;
sub.end = i;
sub.begin = curIndex;
}
}
}
return r;
}
};
4.假設你有一個數組,它的第i個元素是一支給定的股票在第i天的價格。設計一個演算法來找到最大的利潤。你最多可以完成
k
筆交易。
樣例:給定價格 =[4,4,6,1,1,4,2,5]
,
且 k = 2
, 返回 6
.
思路:是題3方法的推廣,需要注意的是在交易已經無法再提高利潤時要退出迴圈以提高演算法速度。網上有討論認為在k大於陣列長度的情況下直接使用題2的方法計算利潤更有效率,也有與我不同的動態規劃思路可以提高效率。參考:http://www.cnblogs.com/grandyang/p/4295761.html
struct Sub{//買賣區間
int begin;
int end;
bool fill;
Sub(int begin = 0, int end = 0, bool fill = true){
this->begin = begin;
this->end = end;
this->fill = fill;
}
};
class Solution {
public:
/**
* @param prices: Given an integer array
* @return: Maximum profit
*/
int maxProfit(int k, vector<int> &prices) {
int max = 0;
vector<Sub> subArr;
subArr.push_back(Sub(0, prices.size() - 1, false));
while(k--){
//尋找一次最有利的切分
int maxCount = 0;
int maxIndex = -1;
Sub maxSub;
Sub tmpSub;
for(int i = 0; i < subArr.size(); ++i){
int tmp;
if(subArr[i].fill){//在實心區間上挖下一個最小值
tmp = -findMinSub(prices, subArr[i].begin, subArr[i].end, tmpSub);
}else{//在空心區間上找最大利潤區間
tmp = findMaxSub(prices, subArr[i].begin, subArr[i].end, tmpSub);
}
if(tmp > maxCount){//記錄下當前最佳劃分方式和其帶來的利潤
maxCount = tmp;
maxIndex = i;
maxSub = tmpSub;
}
}
//計算切分之後的區間狀態
if(maxIndex >= 0){
Sub sub = subArr[maxIndex];
if(sub.fill){
subArr.push_back(Sub(sub.begin, maxSub.begin, true));
subArr.push_back(Sub(maxSub.end , sub.end, true));
maxSub.begin += 1;
maxSub.end -= 1;
subArr[maxIndex] = maxSub;
}else{
subArr.push_back(Sub(sub.begin, maxSub.begin - 1, false));
subArr.push_back(Sub(maxSub.end + 1, sub.end, false));
subArr[maxIndex] = maxSub;
}
//計算此次切分之後的最大利潤
max += maxCount;
}else{ //若無法再切分則退出迴圈
break;
}
}
return max;
}
int findMaxSub(vector<int> &arr, int begin, int end, Sub& sub) {
if(begin >= end)
return 0;
sub.fill = true;
int r = 0;
int curIndex = begin;
for(int i = begin; i <= end; ++i){
if(arr[i] < arr[curIndex]){
curIndex = i;
}else{
int tmp = arr[i] - arr[curIndex];
if(tmp > r){
r = tmp;
sub.end = i;
sub.begin = curIndex;
}
}
}
return r;
}
int findMinSub(vector<int> &arr, int begin, int end, Sub& sub) {
if(begin >= end)
return 0;
sub.fill = false;
int r = 0;
int curIndex = begin;
for(int i = begin; i <= end; ++i){
if(arr[i] > arr[curIndex]){
curIndex = i;
}else{
int tmp = arr[i] - arr[curIndex];
if(tmp < r){
r = tmp;
sub.end = i;
sub.begin = curIndex;
}
}
}
return r;
}
};