您的位置:首页 > 其它

LeetCode简易题解--121、122、123、188

2017-09-18 22:44 267 查看

121. Best Time to Buy and Sell Stock

目标是找到
max(prices[j]−prices[i]), for every i and j such that j > i.


直接用两层循环可能会超时。

更好的方法是遍历一遍
prices[]
数组,每一次遍历执行一下两个操作:

记录下目前为止遇到的最小值

计算当前
price
与最小值的差值,与已经得到的前面部分的最大值作比较,取较大者。

代码如下:

class Solution {
public:
int maxProfit(vector<int>& prices) {
int n = prices.size();
if (n == 1 || n == 0) return 0;
vector<int> res(n, 0);
int min = prices[0];
int final_res = 0;
for (int i = 1; i < n; ++i) {
if (prices[i] < min) min = prices[i];
if (prices[i] - min > final_res) {
final_res = prices[i] - min;
}
}
return final_res;
}
};


122. Best Time to Buy and Sell Stock II

与上一道题同样,是一道简单题,但是直接用两层循环可能会超时。

可以对
prices[]
进行遍历,找到每一段升序子段,结果加上首尾差值即可。

代码如下:

class Solution {
public:
int maxProfit(vector<int>& prices) {
int n = prices.size();
if (n == 1 || n == 0) return 0;
int cur_max, cur_min;
cur_max = cur_min = prices[0];
int final_res = 0;
for (int i = 1; i < n; ++i) {
if (prices[i] > prices[i - 1]) {
cur_max = prices[i];
} else {
final_res += cur_max - cur_min;
cur_min = cur_max = prices[i];
}
}
final_res += prices[n - 1] - cur_min;
return final_res;
}
};


更简洁的方法是:对每一个
prices[i]
,如果
prices[i] - prices[i-1] > 0
,则结果加上该差值。只要后一个数比前一个数大,就加上两数的差值,效果与以上方法是一样的。

123. Best Time to Buy and Sell Stock III

初始收益
profit = 0
,购买时
profit -= prices[i]
,出售时
profit += prices[i]


这里有四个变量:

buy1:表示只买了一块石头的收益

sell1:表示买一块并卖了一块石头的收益

buy2:表示买了第二块石头时的收益,根据题目,此时肯定已经买卖过一次石头了

sell2:表示卖了第二块石头时的收益

prices[]
进行遍历,每一次遍历都计算出以上4个值,则最后的结果必定是
max(sell1, sell2)


class Solution {
public:
int maxProfit(vector<int>& prices) {
if (prices.size() <= 1) return 0;
int buy1 = INT_MIN, buy2 = INT_MIN;
int sell1 = 0, sell2 = 0;
for (int i = 0; i < prices.size(); ++i) {
sell2 = max(sell2, buy2 + prices[i]);
buy2 = max(buy2, sell1 - prices[i]);
sell1 = max(sell1, buy1 + prices[i]);
buy1 = max(buy1, -prices[i]);
}
return sell2 < 0 ? 0 : sell2;
}
};


以上代码的一些解释:

Q: 为何计算顺序是
sell2
buy2
sell1
buy1


A: 因为每一个值都是根据上一步计算的结果推出来的。以
sell2
为例,它是根据上一步的
buy2
推导出来的,若是
buy2
先于
sell2
计算,则在计算
sell2
时,所使用的
buy2
的值是这一步的结果,而不是上一步的结果。其他变量类似。

Q: 为什么直接返回
sell2


A:
sell2
是根据
buy2
计算的,
buy2
是根据
sell1
计算的,这会导致
sell2
始终比
sell1
大。

PS:以上思路来自LeetCode的Disscussion。

188. Best Time to Buy and Sell Stock IV

用动态规划的思路来写。

将买卖一次石头称作一次事务,只有执行至少一次事务时才会有收益。

dp[i][j]
表示:
prices[]
从 0 到 j-1 个元素并且执行了 i 个事务时的最大收益。

首先确定边界:

dp[i][0] = 0 : 第一个元素的地方收益当然为0

dp[0][j] = 0 : 一次事务都没执行,收益当然为0

现在考虑一般情况:

dp[i][j]
表示到
prices[j-1]
并且执行了
j
次事务时的最大收益,该值来源于两个方面:

dp[i][j-1]
:到
prices[j-1]
(即第j天)时,事务数量没有增加(没有卖出石头,收益也就不会增加)

max( prices[j] - prices[jj] + dp[i-1, jj] {0<=jj<j} )
: 到
prices[j-1]
(即第j天)时,事务数量增加1

以上第二个值的解释:
prices[jj]
之前(0到jj-1)视为一个执行了i-1次事务的子段,
dp[i-1][jj]
为此时的最大收益,该值加上jj+1到j之间的最大差值,就可能是
dp[i][j]
。但是这个jj可能会有多个值,因此这些
prices[j] - prices[jj] + dp[i-1, jj] {0<=jj<j}
中的最大值即是
dp[i][j]
的两个可能来源之一。

dp[i][j]
就是以上两个值之中的最大值:

p[i][j] = max(dp[i][j-1], max( prices[j] - prices[jj] + dp[i-1, jj]))
= max(dp[i, j-1], prices[j] + max(dp[i-1, jj] - prices[jj]));


另外,记n为数组长度,则最多有n/2个事务(每个事务至少涉及两个元素),

因此当k>n/2时,该题跟122题是一样的。

代码如下:

class Solution {
public:
int maxProfit(int k, vector<int>& prices) {
if (prices.size() <= 1) return 0;

int n = prices.size();
if (k > n / 2) return quick_solve(prices);

vector<vector<int> > dp(k + 1, vector<int>(n, 0));
for (int i = 1; i <= k; ++i) {
int tmp_max = -prices[0];
for (int j = 1; j < n; ++j) {
dp[i][j] = max(dp[i][j - 1], tmp_max + prices[j]);
tmp_max = max(tmp_max, dp[i - 1][j] - prices[j]);
}
}
return dp[k][n - 1];
}

int quick_solve(vector<int>& prices) {
int n = prices.size();
int res = 0;
for (int i = 1; i < n; ++i) {
if (prices[i] > prices[i - 1])
res += prices[i] - prices[i - 1];
}
return res;
}
};
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: