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. IE中JavaScript 的异常处理

    错误类型 编程中我们会遇到四种错误:编译错误.逻辑错误.输入/验证错误.运行时错误,但是在JavaScript中我们只能最后两种错误. JavaScript 1.5定义了六种错误类型: EvalErr ...

  2. Java 集合系列 10 Hashtable详细介绍(源码解析)和使用示例

    java 集合系列目录: Java 集合系列 01 总体框架 Java 集合系列 02 Collection架构 Java 集合系列 03 ArrayList详细介绍(源码解析)和使用示例 Java ...

  3. poj3669 Meteor Shower(BFS)

    题目链接:poj3669 Meteor Shower 我只想说这题WA了后去看讨论才发现的坑点,除了要注意原点外,流星范围题目给的是[0,300],到302的位置就绝对安全了... #include& ...

  4. python 循环设计

    for循环 1.range()用法 for循环后的in跟随一个序列的画,循环每次使用的序列元素而不是序列的下标 例:s='abcdefg' for i in range(0,len(s),3): pr ...

  5. Q: How could I use MATLAB interface for parameter selection?

    Q: How could I use MATLAB interface for parameter selection? One can do this by a simple loop. See t ...

  6. 百度360争推1TB永久网盘

    导读:百度云.360云盘的互掐则将云盘空间战拉升到史无前例的高度——无论是国内还是国外,还没有哪家公司有过这样的手笔.      这几天百度热热闹闹开大会,会场外“小伙伴们”也不甘寂寞.       ...

  7. 如何整治那些敢偷用你Wi-Fi的人

    我的邻居正在盗用我的WiFi,唔,对此我可以直接选择加密口令,或者…作为一名极客我也可以耍耍他.那么,我就从划分网络开始吧.我把网络划分成两部分,受信任部分和非受信任部分.受信任部分组成一个子网,而非 ...

  8. jmeter之json数据参数化 断言等

    在 http Load Testing 中,json 数据的提交是个让人头疼的问题.本文详细介绍如何进行 JMeter 的 json 测试提交,以及如何将其参数化.Step 1 http json 请 ...

  9. WP8.1 Study3:WP8.1中Animation应用

    WP8.1上的Animation动画的API和WIN8/WIN8.1上的差不多,网上可以找到很多资料,同时可以去MSDN看官方文档. 下面是我参考一些资料,写出来的例子,希望以后有用. xaml代码如 ...

  10. bitset常用函数用法记录 (转载)

    有些程序要处理二进制位的有序集,每个位可能包含的是0(关)或1(开)的值.位是用来保存一组项或条件的yes/no信息(有时也称标志)的简洁方法.标准库提供了bitset类使得处理位集合更容易一些.要使 ...