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. ubuntu安装SCrapy

    依次安装 sudo apt-get install build-essential; sudo apt-get install python-dev; sudo apt-get install lib ...

  2. 51nod 1613翻硬币

    题目链接:51nod 1613 翻硬币 知乎上的理论解法http://www.zhihu.com/question/26570175/answer/33312310 本题精髓在于奇偶性讨论. 若 n ...

  3. 表单重置reset

    最近一直在做各种页面的“增删改查”,只是在做新增功能的时候,自己一直在使用 reset来清空form表单,原以为这样子的清空方法是万无一失的,可惜最终还是在进行“修改”操作完了之后再“新增”的时候,就 ...

  4. mongodb 和 mysql 的对照

    In addition to the charts that follow, you might want to consider the Frequently Asked Questions sec ...

  5. 微信支付官方SDK V3 .NET版的坑

    但是支付成功后却不能正确的执行支付结果js回调函数.看看其页面的点击事件是放在asp:Button上面的.我们知道在asp.net webform中,按钮的点击是有页面回调后台的.也就是其实点击了之后 ...

  6. Zabbix页面遇到历史记录的乱码需要修改数据库

    Zabbix页面遇到历史记录的乱码需要修改数据库: 解决办法: 1.将 zabbix 数据库中的表备份: 2.手动删除 zabbix 数据库: 3.重新创建 zabbix 库时手动指定字符集为 utf ...

  7. ASP.NET MVC学习之路由篇(2)

    7.解决与物理路径的冲突 当发送一个请求至ASP.NET MVC时,其实会检查网站中存不存在这个请求的物理路径文件,如果存在的话,就会直接将这个物理文件返回.但是有时候我们需要它执行控制器的某个方法, ...

  8. js获取URL地址中的GET参数

    var $_GET = (function(){ var url = window.document.location.href.toString(); var u = url.split(" ...

  9. 蓝桥杯 algo_5 最短路 (bellman,SPFA)

    问题描述 给定一个n个顶点,m条边的有向图(其中某些边权可能为负,但保证没有负环).请你计算从1号点到其他点的最短路(顶点从1到n编号). 输入格式 第一行两个整数n, m. 接下来的m行,每行有三个 ...

  10. Program A-归并排序

    Description In this problem, you have to analyze a particular sorting algorithm. The algorithm proce ...