Best Time to Buy and Sell Stock IV

Say you have an array for which the ith element is the price of a given stock on day i.

Design an algorithm to find the maximum profit. You may complete at most k transactions.

Note:
You may not engage in multiple transactions at the same time (ie, you must sell the stock before you buy again).

Credits:
Special thanks to @Freezen for adding this problem and creating all test cases.

[Solution]

这题如果一开始不重视 k 的条件限制,很容易误以为是一道简单的贪心题。

大概会想,无非就是找出序列中 所有的上升区段,并用上升区段的最后一个数减第一个数(即此上升区段的跨度)作为其 value,最后再取 前 k 个最大的 values 求和得解。

典型的贪心思想,我一开始这样想写的代码如下:(注意:一次交易是指一次买入一次卖出操作,题目未说明清楚。)

 class Solution:
# @return an integer as the maximum profit
def maxProfit(self, k, prices):
if prices == []:
return 0
down = prices[0]
up = prices[0]
ansList = []
for i in range(1, len(prices)):
if prices[i-1] < prices[i]:
up = prices[i]
elif prices[i] < prices[i-1]:
ansList.append(up - down)
down = prices[i]
up = prices[i]
ansList.append(up - down)
ansList.sort(reverse=True)
if k < len(ansList):
ans = sum(ansList[:k])
else:
ans = sum(ansList)
return ans

而且这贪心可以过:200 / 211 test cases passed.

但细想一下,正是因为有 k 的限制,其实 贪心 是不对的。

考虑这样一个样例:

Input:   2, [1,2,4,2,5,7,2,4,9,0]

如果用上面那段代码得出的结果会是:12,显然是取了 3~5 和 6~8 这两个区段(list下标号)。

但实际上,因为 k 限制在 2,所以可以有更优的解:0~5 和 6~8: 6+7=13。

这样,我们就找了一个反例证明贪心的不正确性(其实也不难直观的去想到)。

实际上,由于 k 的限制,使得决策之间产生了联系,决定了贪心的错误性。

很自然的,做决策求最值,会想到用 DP (动态规划)来完成这道题。

再来思考问题的本质,可以发现这样的特点:

买入和卖出操作是交替执行的,而且卖出之前一定得先买入。

转化为这样的一个数学问题:

给定一个序列,从该序列中找出一个元素保持在原序列中顺序并且元素个数是偶数的子序列,使得其偶数项之和与奇数项之和的差最大。

而对于每一天来说,只有两种选择,即选择 操作 或者 不操作。而具体的操作是买入还是卖出得由当前已经执行的操作数是奇偶性来决定。

所以我们可以从天数递增来地推上去直到第n天。

如果用 dp[j] 表示 完成第j次交易时 (由 j-1 转移到 j 必需伴随着一次操作)的最大收益,则状态转移方程可以写为:

dp[j] = max{dp[j], dp[j-1] + prices[i] * (j%2)?-1:1}

外循环 天数 i 从 0 递推到 size (第 j 次交易不一定发生在哪一天),内循环 j 从 1 递推到当前允许的最大操作数:min{2*k, i+1} (i 是 prices 中的 第 i+1 个元素)。

初始条件:dp[0] = 0,dp中的其他值为无穷小(None比任何数值都小)。

另外,有一个优化,当 2*k >= szie 时,k 的限制已经形同虚设了,操作数限制已经可以覆盖到每一天都允许操作,这种情况下还用上述DP效率太低,可以改为没有k限制的贪心版本(把所有上升区段跨度求和即为解)。

由于有了上述优化,所以用DP的情况一定是 2*k < size的情况,这种情况下 用完 2*k 次操作数限制所得的结果一定等于最优解(虽然不一定实际上进行2*k次操作,因为此时实际上放宽了题目限制条件,在同一个 i 值,dp[j]可能把这个 i 买入使得当前更优(因为初始值为无穷小),而到了dp[j+1]就把这个 i 卖出使得当前更优,实际上就相当于不操作,但记录的操作数 j 还是增加了2,所以实际上此时dp[j]表示的准确的实际含义是 最多完成j次交易时(实际可能不在j次时是最优),放大到全局即为 2*k 处最优。而又因为 i 是递推上去到 n 的,所以总会有找到 正确的最后一次交易使得在第 i 天dp[2*k]最大,而 i 再增加,操作数已经不能增加了(已经到了最大的限制)),所以最后的结果为 dp[2*k]。

最后AC代码:

 class Solution:
# @return an integer as the maximum profit
def maxProfit(self, k, prices):
size = len(prices)
if k*2 >= size:
return self.noKLimit(size, prices)
dp = [None] * (2*k+1)
dp[0] = 0
for i in range(size):
for j in range(1, min(2*k, i+1)+1):
dp[j] = max(dp[j], dp[j-1]+prices[i]*[1, -1][j%2])
return dp[2*k] def noKLimit(self, size, prices):
sum = 0
for i in range(size - 1):
if prices[i+1]>prices[i]:
sum += prices[i+1] - prices[i]
return sum

注:一开始做的时候还没发现原来这是一个系列题,这是目前该系列最后一个(第四题),应该递进的来做可能比较简单。

参考:[LeetCode]Best Time to Buy and Sell Stock IV

【LeetCode】Best Time to Buy and Sell Stock IV的更多相关文章

  1. 【leetcode】Best Time to Buy and Sell Stock III

    Best Time to Buy and Sell Stock III Say you have an array for which the ith element is the price of ...

  2. 【leetcode】Best Time to Buy and Sell Stock II

    Best Time to Buy and Sell Stock II Say you have an array for which the ith element is the price of a ...

  3. 【leetcode】121-Best Time to Buy and Sell Stock

    problem 121. Best Time to Buy and Sell Stock code class Solution { public: int maxProfit(vector<i ...

  4. 【LeetCode】Best Time to Buy and Sell Stock

    Say you have an array for which the ith element is the price of a given stock on day i. If you were ...

  5. Java for LeetCode 188 Best Time to Buy and Sell Stock IV【HARD】

    Say you have an array for which the ith element is the price of a given stock on day i. Design an al ...

  6. [LeetCode] 188. Best Time to Buy and Sell Stock IV 买卖股票的最佳时间 IV

    Say you have an array for which the ith element is the price of a given stock on day i. Design an al ...

  7. 【数组】Best Time to Buy and Sell Stock I/II

    Best Time to Buy and Sell Stock I 题目: Say you have an array for which the ith element is the price o ...

  8. [LeetCode][Java] Best Time to Buy and Sell Stock IV

    题目: Say you have an array for which the ith element is the price of a given stock on day i. Design a ...

  9. 【leetcode】Best Time to Buy and Sell 3 (hard) 自己做出来了 但别人的更好

    Say you have an array for which the ith element is the price of a given stock on day i. Design an al ...

随机推荐

  1. 121. 122. 123. 188. Best Time to Buy and Sell Stock *HARD* 309. Best Time to Buy and Sell Stock with Cooldown -- 买卖股票

    121. Say you have an array for which the ith element is the price of a given stock on day i. If you ...

  2. java下载网络图片

    import java.io.DataInputStream;import java.io.File;import java.io.FileOutputStream;import java.io.IO ...

  3. Java 正则表达式 向前、向后匹配

    //向后匹配 String a = "I paid $90 for 10 oranges, 12 pears and 8 apples. I saved $5 on "; Patt ...

  4. nyoj-746

    整数划分(四) 时间限制:1000 ms  |  内存限制:65535 KB 难度:3   描述 暑假来了,hrdv 又要留学校在参加ACM集训了,集训的生活非常Happy(ps:你懂得),可是他最近 ...

  5. R中rJava包载入时报错的问题

    今天安装XLConnect包,安装后无法library(XLConnect)载入,看报错问题应该出在rJava上,找到了下面的解决办法: if (Sys.getenv("JAVA_HOME& ...

  6. 无需激活用户直接登入discuz

    //打开discuz/api/uc.php //synlogin方法(180行)处,往下找到 if(($member = getuserbyuid($uid, 1))) { dsetcookie('a ...

  7. python使用urllib2抓取网页

    1.使用python的库urllib2,用到urlopen和Request方法. 2.方法urlopen原形 urllib2.urlopen(url[, data][, timeout]) 其中: u ...

  8. html标签marquee实现走马灯效果(文字浮动)

    marquee标签实现文字或图片的移动效果 <marquee direction = "right">文字<marquee>

  9. bzoj 1997: [Hnoi2010]Planar

    #include<cstdio> #include<cstring> #include<iostream> #define M 20005 #define N 20 ...

  10. ZOJ 3646 Matrix Transformer 二分匹配,思路,经典 难度:2

    http://acm.zju.edu.cn/onlinejudge/showProblem.do?problemId=4836 因为要使对角线所有元素都是U,所以需要保证每行都有一个不同的列上有U,设 ...