Leetcode309-Best Time to Buy and Sell Stock with Cooldown图文详解

网上关于本题的解读,或是状态机,或是动态规划,但是大多都只是把公式列出来,很抽象,很少回答为什么该解法能求出最终解,本人花了些功夫分析了本题的本质,结合网上各路博客的解读,画了些图以帮助大家理解。

一,状态转换

首先,对于任意i天开始(注意是第i天开始,第i天的操作还没发生)的时候,有三种可能的状态:

S0,手中没有股票,可以买;S1,手中有股票,可以卖;S2,手中没有股票,也不能买,即昨天刚卖,今天cooldown;

在这三种状态下,分别可以执行买卖等操作,即第i天的操作之后,到达第i+1天的状态,转换图如下:

 

由此,我们从第1天(下标为0)开始,画出全部的状态转换图,如下:

 

该图中,每一条从0到n的路径表示一种可能的交易方式,则该状态转换图包含了全部可能的交易方式。这就是解空间,有了它我们才能想办法在其中求一个最大值,接下来就是动态规划的内容了。

二,动态规划

上一段中的状态好理解,但是在程序中如何表示和实现呢?尤其是,如何由这些状态得出最终我们想要的最大收益值呢?

按照暴力的解法思考,上面已经表示出了所有的路径,到第i天的不同状态有多种路径,每种路径最终的导致的收益都不一样。现在我们只存储到第i天的不同状态的所有路径中的最大的收益值,那么就来到了很多解法都提到的3个状态数组,我们对应于3种状态先设下3个数组,每个数组第i项存储的是:到第i天开始时候,如果是该状态,此时最大收益(注意此时第i天的操作依然没发生,所以最后的最大值还要考虑该天的操作,先按下不表)。这个最大值的求法就是我们熟悉的动态规划了。如下图:

假设我们已经求出了第i天的3种状态下的最大收益值,即三个数组中的第i项。第i+1天的最大值,只由第i天的值决定,即是如下三个公式:

s0[i+1] = max(s0[i], s2[i]);

s1[i+1] = max(s0[i] - prices[i], s1[i]);

s2[i+1] = s1[i] + prices[i];

这里可以先缓一缓,思考一下这里第i+1项是否只由第i项决定,因为这是该题能用动态规划求解的基础。动态规划的定义是,原问题可以转化为一个或多个更小的问题来接。在本题中,第i+1天的最大值,无论它是从哪条路径过来的,都要经过第i天的3中状态之一。假如第i+1天的最大值,不对应于第i天的最大值之一,那么它就不是最大值,因为我们能从第i天的最大值中求出一个更大的i+1天的值。如此则动态规划成立。

接下来的事就好办了,按照公式递推,就能求得最终第i天开始时的最大值。其中开始的几天可以按照边界条件来处理,也可以虚构一些第1天之前的值,使转换图完美循环(我是不太喜欢这种做法),最后,数组存储的是最后一天开始时候的值,最后一天还可能有操作,如下图:

 

最后一天应该不会买,只会卖了,比较,取最大值就行了。

max(max(s0[len-1], s2[len-1]), s1[len-1] + prices[len-1]);//len就是数组长度,就是天数

附上AC代码:

 1 class Solution {
 2 public:
 3     int maxProfit(vector<int>& prices) {
 4         int len = prices.size();
 5         if(len == 0 || len == 1)
 6             return 0;
 7         if(len == 2)
 8             return prices[1] > prices[0] ? prices[1] - prices[0] : 0;
 9         
10         vector<int> s0(len);
11         vector<int> s1(len);
12         vector<int> s2(len);
13         s0[2] = 0;
14         s1[2] = max(-prices[1], -prices[0]);
15         s2[2] = prices[1] - prices[0];
16         for(int i = 3; i < len; i++)
17         {
18             s0[i] = max(s0[i-1], s2[i-1]);
19             s1[i] = max(s0[i-1] - prices[i-1], s1[i-1]);
20             s2[i] = s1[i-1] + prices[i - 1];
21         }
22         return max(max(s0[len-1], s2[len-1]), s1[len-1] + prices[len-1]);
23     }
24 };

 

网上还有很多优化方法,比如转为两个状态的,以及取消数组的,我是懒得想了,把问题弄清楚最重要。

 

 

 

posted on 2017-11-10 11:14  cestlav  阅读(169)  评论(0)    收藏  举报

导航