今天做了两道动态规划的题目,感觉对动态规划有了更深入的了解。废话不多说,这是第一道。题目要求如下:
假设你有一个数组,它的第i个元素是一支给定的股票在第i天的价格。设计一个算法来找到最大的利润。你最多可以完成两笔交易。
注意事项:
你不可以同时参与多笔交易(你必须在再次购买前出售掉之前的股票)
给出样例如下:
给出一个样例数组 [4,4,6,1,1,4,2,5],返回 6
题目给出的框架如下:
class Solution {
public:
/** * @param prices: Given an integer array * @return: Maximum profit */
int maxProfit(vector<int> &prices) {
// write your code here
}
};
一开始的想法是用两个数组buy[]和sell[]来解决,状态转移方程为:
buy[i] = max(buy[i-1],sell[i-1] - prices[i])
sell[i] = max(sell[i-1],buy[i-1] + prices[i])
最后返回sell[len-1],其中 i 为当前天数,len为总共的天数。
想法很简单,将当前状态分为已经买入股票和已经卖出股票这两种:
1. 如果第 i 天的状态为买入股票的话, 要么就是之前买了股票,第i天不买,对应于buy[i-1];要么就是之前卖了股票,第i天买入股票,对应于sell[i-1] - prices[i])。
2. 同理, 如果第 i天的状态为卖出股票的话,要么就是之前卖了股票,第i天不卖,对应于sell[i-1];要么就是之前买了股票,第i天卖掉,对应于buy[i-1]+ prices[i]。
但这忽视了一个条件:
你最多可以完成两笔交易!
因此,上面的状态转移方程解决的问题应该是 :
不管你交易多少笔,你的最大获利是多少!
这是因为你没有限制交易多少笔,所以只要能获利,我就可以将交易增加到3次,4次甚至更多次,sell[len]的值也会相应的增加。
既然你说只能交易两次,那么我就对buy和sell的两次交易分别用两个数组存不就行了?!说做就做,状态转移方程如下:
firstBuy[i] = max(firstBuy[i - 1], -prices[i])
firstSell[i] = max(firstSell[i - 1], firstBuy[i - 1] + prices[i])
secondBuy[i] = max( secondBuy[i - 1], firstSell[i - 1] - prices[i])
secondSell[i] = max( secondSell[i - 1], secondBuy[i - 1] + prices[i])
方程很好理解:
1. 如果第 i 天为第一次买的状态,要么就是之前买过了,第i天不卖,对应于firstBuy[i -1],要么就是之前没有买,第i天买入股票,对应于-prices[i];
2. 如果第 i 天为第一次卖的状态,要么就是之前卖出了,第i天不买,对应于firstSell[i -1],要么就是之前买入了股票,第i天卖出股票,对应于firstBuy[i - 1] + prices[i];
3. 如果第 i 天为第二次买的状态,要么就是之前已经二次买过了,第i天不卖,对应于secondBuy[i -1],要么就是之前卖过一次了,第i天再次买入股票,对应于firstSell[i - 1] - prices[i];
4. 如果第 i 天为第二次卖的状态,要么就是之前已经二次卖过了,第i天不买,对应于secondSell[i -1],要么就是之前第二次购买,第i天卖出股票,对应于secondBuy[i - 1] + prices[i];
最后,还需要对第一天的买卖进行初始化:
firstBuy[0] = -prices[0]
secondBuy[0] = -prices[0]
firstSell[0] = 0
secondSell[0] = 0
代码实现如下:
class Solution {
public:
/** * @param prices: Given an integer array * @return: Maximum profit */
int maxProfit(vector<int> &prices) {
// write your code here
const int len = prices.size();
if (len == 0)
return 0;
int firstBuy[len];
int secondBuy[len];
int firstSell[len];
int secondSell[len];
firstBuy[0] = -prices[0];
secondBuy[0] = -prices[0];
firstSell[0] = 0;
secondSell[0] = 0;
for (int i = 1; i < len; ++i) {
firstBuy[i] = max(firstBuy[i - 1], -prices[i]);
firstSell[i] = max(firstSell[i - 1], firstBuy[i - 1] + prices[i]);
secondBuy[i] = max(secondBuy[i - 1], firstSell[i - 1] - prices[i]);
secondSell[i] = max(secondSell[i - 1],
secondBuy[i - 1] + prices[i]);
}
return secondSell[len - 1];
}
};