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. 错误 undefined reference to __cxa_guard_acquire/release

    用gcc编译 c++ 程序时,出现错误 undefined reference to __cxa_guard_acquire linker error, 但是用icc可以正常编译, 问题出在stati ...

  2. HDU----(4519)郑厂长系列故事——体检

    郑厂长系列故事——体检 Time Limit: 500/200 MS (Java/Others)    Memory Limit: 65535/32768 K (Java/Others)Total S ...

  3. hiphop-php 搭建 (CentOS)

    前提:只支持64位系统 升级GCC: https://github.com/jackywei/HOW-TO-BUILD-HHVM-WiKi/wiki/Build-&-Install-GCC4. ...

  4. Eclipse上安装GIT插件EGit

    一.Eclipse上安装GIT插件EGit Eclipse的版本eclipse-java-helios-SR2-win32.zip(在Eclipse3.3版本找不到对应的 EGit插件,无法安装) E ...

  5. 读书笔记2:HTTP协议

    HTTP是什么 HTTP定义 HTTP( Hypertext Transfer Protocol, 超文本传输协议) 是在万维网上进行通信时所使用 的协议方案. HTTP的地位 了解HTTP协议的地位 ...

  6. 客户信贷管理&临时授信

    信贷额度的组成:假如某客户信用限额1万:开出销售订单时锁定1万:一旦发货1万,销售订单1万限额释放,变成发货锁定限额1万.一旦开票,发货1万限额释放,应收锁定1万限额.清帐成功,应收释放1万.信用限额 ...

  7. javascript function new this

    1. 首先,我们这里把function直接调用时将这个function当做方法来看待,而new function是将function当做类来看待 2. 当把function作为类来使用时,functi ...

  8. kafka客户端代码解析

    转载:http://backend.blog.163.com/blog/static/202294126201431724652597/ 可以使用服务器端下载的kafka二进制包及依赖,也可以通过ma ...

  9. GridControl的用法(1)

    一.属性设置 ①去除gridControl上的筛选条 //去除上面的筛选条            gridView1.OptionsView.ShowGroupPanel = false; ②设置列名 ...

  10. PHP+mysql常用类库

    <?php /** * @title: Ekcms mysql类库 * @version: 1.0 * @author: perry <perry@1kyou.com> * @pub ...