1.Description
leetcode-121. Best Time to Buy and Sell Stock(DP)
Say you have an array for which the ith element is the price of a given stock on day i.
If you were only permitted to complete at most one transaction (ie, buy one and sell one share of the stock), design an algorithm to find the maximum profit.
Example 1:
Input: [7, 1, 5, 3, 6, 4]
Output: 5
max. difference = 6-1 = 5 (not 7-1 = 6, as selling price needs to be larger than buying price)
Example 2:
Input: [7, 6, 4, 3, 1]
Output: 0
In this case, no transaction is done, i.e. max profit = 0.
2.Analysis
无非是找到后面的数相对当前的差距最大。算法的复杂度还是O(n2),暂时想不出好的方法,刚接触DP。很显然第一种解法不是dp方法。
3.code
class Solution {
public:
int maxProfit(vector<int>& prices) {
int max = 0;
int index = 0;
for(int i = 0; i < prices.size(); ) {
if(prices[i+1] < prices[i]) {
//如果下一个小于当前的话,跳到下一个,避免无用的内嵌套循环
i++;
} else {
for(int j = i+1; j < prices.size(); j++) {
int tmp = prices[j] - prices[i];
if( tmp > 0 && tmp > max) {
max = prices[j] - prices[i];
index = j;
}
}
i++;
}
}
return max;
}
};
4. improvement
想了很久,熟悉了dp问题的一些相关概念,比如:状态、状态转移、状态转移方程,想到在这道题中,这三者分别是什么呢。从题目给的数据似乎不能只直观地看出来这些变量。所以我构造了一个数组,来观察是否能构造出“状态”。
int diff[n];
for (int i = 1; i < prices.size(); i++) {
diff[i] = prices[i] - prices[i-1];
}
所以,我把每一个元素减去前面的元素作为新数据的元素(也就是后一天相比前一天的估值变化,除去第一天),这样,显然可以看到“状态”了,那就是要么“涨”,要么“贬”。而我们对这个问题分“阶段”,因为dp问题是“多阶段”的,那么这里的“阶段”是什么呢?比如说,给定一个数据 [1,2,3,4,5],显然diff的元素都是正的,那么这些元素都可以归结到同一个阶段,也就是说连续为正的子集是该问题的“阶段”,如果给定数组[1,2,3,4,5,8,6,7,10,11],对应的diff数组为[0,1,1,1,1,3,−2,1,3,1], 那么,当元素为6时,该位置的diff是负数,所以出现了“贬”的状态,而在此之前,最大的收益是 8−1=7=0+1+1+1+1+3,此后,该阶段的最大值可能变小,所以这可能就是”状态”要转移了,也就是阶段行的最大值可能发生变化。而当到达元素10的时候,[1...10] 的阶段性最大值又超过了 [1...8] 的最大值,此时我们可以认为状态发生了转移。所以导致状态发生转移的条件是当前的diff子列的和已经无法再长大,并且开始萎缩,甚至萎缩到小于0【见下面解释】
count += diff[index];
maxNum = max(count, maxNum);
也就是我们连续计算一个diff子列的和,而且始终保持记录当前出现过的子列的阶段性最大值maxNum(备忘录法)为该子列的和就很好地表示了最大收益。如:
而这刚好就是diff数组。所以我们的构造方法是对的,很好地刻画了最大收益。
while(index < prices.size()) {
count += diff[index];
maxNum = max(count, maxNum);
if(count < 0)
count = 0;
index ++;
}
【解释】:当count萎缩到小于等于等于0的时候(要发生状态转移),必然存在一个使之萎缩的diff子列,而在这个子列当中,阶段性最大值max不可能再长大(否则不会萎缩到0,使之长大的必然在长大阶段的子列中)
所以最后的出来的DP解法如下:
#include <iostream>
using namespace std;
int maxProfit(vector<int>& prices) {
if(prices.size() <= 1) return 0;
int diff[n];
for (int i = 1; i < prices.size(); i++) {
diff[i] = prices[i] - prices[i-1];
}
int count = 0, maxNum = 0, index = -1;
//找出diff中第一个不为负数的元素,开始进入长大阶段
for(int k = 1; k < prices.size(); k++) {
if(diff[k] > 0) {
index = k;
break;
}
}
//如果diff元素都为非正数数,也就是原数组的元素是非递增的
if(index == -1) return 0;
while(index < prices.size()) {
count += diff[index];
maxNum = max(count, maxNum);
if(count < 0)
count = 0;
index ++;
}
return maxNum;
}
int max(int a, int b) {
return (a >= b)? a:b;
}
5. others’ perfect solution
同样的维护一个当前的最大值,记录出现的更小的值,从而等待更大的最大值。
class Solution {
public:
int maxProfit(vector<int>& prices) {
int minprice = INT_MAX;
int maxprofit = 0;
for (int i = 0; i < prices.size(); i++) {
if (prices[i] < minprice)
minprice = prices[i];
else if (prices[i] - minprice > maxprofit)
maxprofit = prices[i] - minprice;
}
return maxprofit;
}
};