今天我做了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;
}
};