Python入门习题(90)——OpenJudge百练习题:股票买卖

OpenJudge百练第4121号习题:股票买卖

题目描述

来源
OpenJudge网站 —— 百练习题集-第4121号习题

要求
总时间限制: 1000ms 内存限制: 65536kB

描述
最近越来越多的人都投身股市,阿福也有点心动了。谨记着“股市有风险,入市需谨慎”,阿福决定先来研究一下简化版的股票买卖问题。

假设阿福已经准确预测出了某只股票在未来 N 天的价格,他希望买卖两次,使得获得的利润最高。为了计算简单起见,利润的计算方式为卖出的价格减去买入的价格。

同一天可以进行多次买卖。但是在第一次买入之后,必须要先卖出,然后才可以第二次买入。

现在,阿福想知道他最多可以获得多少利润。

输入
输入的第一行是一个整数 T (T <= 50) ,表示一共有 T 组数据。
接下来的每组数据,第一行是一个整数 N (1 <= N <= 100, 000) ,表示一共有 N 天。第二行是 N 个被空格分开的整数,表示每天该股票的价格。该股票每天的价格的绝对值均不会超过 1,000,000 。
输出
对于每组数据,输出一行。该行包含一个整数,表示阿福能够获得的最大的利润。
样例输入
3
7
5 14 -2 4 9 3 17
6
6 8 7 4 1 -2
4
18 9 5 2
样例输出
28
2
0
提示
对于第一组样例,阿福可以第 1 次在第 1 天买入(价格为 5 ),然后在第 2 天卖出(价格为 14 )。第 2 次在第 3 天买入(价格为 -2 ),然后在第 7 天卖出(价格为 17 )。一共获得的利润是 (14 - 5) + (17 - (-2)) = 28
对于第二组样例,阿福可以第 1 次在第 1 天买入(价格为 6 ),然后在第 2 天卖出(价格为 8 )。第 2 次仍然在第 2 天买入,然后在第 2 天卖出。一共获得的利润是 8 - 6 = 2
对于第三组样例,由于价格一直在下跌,阿福可以随便选择一天买入之后迅速卖出。获得的最大利润为 0

解题思路

  1. 假设在第d天进行第2次的买入,有:最大利润 = “第1天到第d天之间进行一次买卖的最大利润” + “第d天买入之后卖出的最大利润”。令d依次取1, 2, …, N,求出第d天买入情形下的最大利润,这N个利润中的最大值就是最终结果。这里,第d天买入都是指第二次买入,下同。
  2. 先求“第d天买入之后卖出的最大利润”。有:“第d天买入之后卖出的最大利润” = “第d天之后的最高价格” - 第d天的价格。
  3. 如何求第d天之后的最高价格?“第d天之后的最高价格 ” = max(“第d天的价格”,“第d+1天之后的最高价格”),max函数是取最大值。从右向左扫描,能够依次递推得到第N, N-1, …, 1天之后的最高价格。具体步骤是:
    (1)第N天之后的最高价格是第N天的价格。
    (2)第N-1天之后的最高价格=max(“第N-1天的价格”, “第N天之后的最高价格”),
    (3)第N-2天之后的最高价格=max(“第N-2天的价格”,“第N-1天之后的最高价格”),
    (4)…
    (5)第1天之后的最高价格=max(“第1天的价格”, “第2天之后的最高价格”)。
  4. 如何求 “第1天到第d天之间进行一次买卖的最大利润” ? “第1天到第d天之间进行一次买卖的最大利润” = max(MF[d],“第1天到第d-1天之间进行一次买卖的最大利润”)。这里,MF[d]是第d天卖出产生的最大利润。
  5. “第d天卖出的最高利润MF[d]" = “第d天的价格” - “第d天之前的最低价格”。
  6. “第d天之前的最低价格” = min(第d天的价格,“第d-1天之前的最低价格”)。min函数是取最小值。从左向右扫描,能够依次递推得到第1, 2, …, N天之前的最低价格。做法与上面第3条有类似之处。
  7. 有了第1, 2, …, N天之前的最低价格,我们能够计算得出第1天到第d天(d=1, 2, …, N)之间进行一次买卖的最大利润。
    (1)第1天到第1天之间进行一次买卖的最大利润是0。
    (2)第1天到第2天之间进行一次买卖的最大利润 = max(MF(2),第1天到第1天之间进行一次买卖的最大利润)
    (3)第1天到第3天之间进行一次买卖的最大利润 = max(MF(3),第1天到第2天之间进行一次买卖的最大利润)。
    (4)…
    (5)第1天到第N天之间进行一次买卖的最大利润 = max(MF(N),第1天到第N-1天之间进行一次买卖的最大利润)。
  8. 以上算法步骤,时间开销是O(N)级的。注意:题目指出,N最大是100, 000,O(N)级算法能通过,而O(N2)级的算法将出现超时。

参考答案

#ns存储有N天的股票价格,求出买卖两次的最大利润
def most_profit(ns):
    the_most = 0

    #求出左侧的最小价格
    left_least = [0] * len(ns)
    left_least[0] = ns[0]
    for i in range(1, len(ns)):
        if left_least[i-1] > ns[i]:
            left_least[i] = ns[i]
        else:
            left_least[i] = left_least[i-1]

    #求出一次买卖后左侧的最大利润
    left_most_profit = [0] * len(ns)
    left_most_profit[0] = 0
    #在第i天左侧的最大利润
    for i in range(1, len(ns)):
        profit_i = ns[i] - left_least[i]
        if profit_i > left_most_profit[i-1]:
            left_most_profit[i] = profit_i
        else:
            left_most_profit[i] = left_most_profit[i-1]

    #求出右侧的最高价格
    right_largest = [0] * len(ns)
    right_largest[-1] = ns[-1]
    for i in range(len(ns)-2, -1, -1):
        if right_largest[i+1] < ns[i]:
            right_largest[i] = ns[i]
        else:
            right_largest[i] = right_largest[i+1]

    for second_buy_day in range(len(ns)):
        #假设第二次买入发生在第second_buy_day+1天
        first = left_most_profit[second_buy_day]
        second = right_largest[second_buy_day] - ns[second_buy_day]
        current_most = first + second
        if current_most > the_most:
            the_most = current_most
    return the_most

T = int(input())
for t in range(T):
    N = int(input())
    ns = [int(s) for s in input().split()]
    print(most_profit(ns))

测试用例

  1. 题目描述给出的测试用例包含3组测试数据。第1组测试用例覆盖了第一次买,第一次卖,第二次买,第二次卖,都不在同一天的情形。第2组测试用例覆盖了第二次买和第二次卖在同一天的情形。第3组测试用例覆盖了第一次买,第一次卖,第二次买,第二次卖都在同一天的情形。

  2. T=1,N=1的边界情形。
    样例输入
    1
    1
    5
    样例输出
    0

  3. 第一次买,第一次卖在同一天的情形。
    样例输入
    1
    3
    5 1 8
    样例输出
    7

小结

  1. 本题要求算法的时间开销级别是O(N)级。因为N最大是100,000。
  2. 算法的核心有两点。一是,从左到右地,求出第d天之前进行一次买卖的最大利润,d=1, 2, …, N。二是,从右到左地,求出第d天进行第二次买入之后卖出的最大利润。
  3. 算法具有递推的特征。
  • 1
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值