股票买卖

题目链接

题目描述

最近越来越多的人都投身股市,阿福也有点心动了。谨记着“股市有风险,入市需谨慎”,阿福决定先来研究一下简化版的股票买卖问题。
假设阿福已经准确预测出了某只股票在未来N天的价格,他希望买卖两次,使得获得的利润最高。为了计算简单起见,利润的计算方式为卖出的价格减去买入的价格。
同一天可以进行多次买卖。但是在第一次买入之后,必须要先卖出,然后才可以第二次买入。
现在,阿福想知道他最多可以获得多少利润。

输入

输入的第一行是一个整数T(T≤50),表示一共有T组数据。
接下来的每组数据,第一行是一个整数N(1≤N≤100,000),表示一共有N天。第二行是 N 个被空格分开的整数,表示每天该股票的价格。该股票每天的价格的绝对值均不会超过1,000,000。

输出

对于每组数据,输出一行。该行包含一个整数,表示阿福能够获得的最大的利润。

样例输入

样例输出

在这里插入图片描述

详细思路

用price[i]表示每天的价格

1.先思考买卖一次最大利润:

先看两个数的时候:

5 14 可得出f[1][2]=9

再看三个数:

5 14 -2 可得出f[1][3]=9

即要判断是否要换到-2卖,即判断在-2卖利润大还是在14卖利润大。

用f[i][j]表示在i-j段最大化的利润(不一定是在i买j卖)

由于买入时机不能简单的判断为i-j段价格最小的时候,所以每新加一个数,就要判断新进的数是否要参与买卖,判断方法是分别算出参与和不参与的最大利润,取较大的那个:

如果参与买卖,即在第j天卖出,

这时候要是要想利润最大化,一定是在i-j中的price[t]min处买入,所以我们需要设置一个min[i][j]

如果不参与,利益最大仍为f[i][j-1]

则总结得:

即 f[i][j]=max{f[i][j-1],price[j]-min[i][j]}

2.再考虑两次买卖:

关键是要想到:

无论怎么安排两次买卖,总能将所有数据分为两段,每段各包含一次完整的买卖

所以可以分别从两头开始,用买卖一次的方法都算一遍,然后比较断点在哪里利益最大。

由于是都是从端点开始的,所以f一维就够了,即默认i=1/j=n

代码实现

#include<iostream>
#include<cstring>
#include<cmath>
#include<algorithm>
#include<cstdio>
using namespace std;
int main()
{
    int t;
    cin>>t;
    for(int k=1;k<=t;k++)
    {
        int n,pri[100005],_min[100005],_max[100005],fgo[100005],fback[100005];
        cin>>n;
        for(int i=1;i<=n;i++)
        {
            cin>>pri[i];
            if(i==1)  //得到1-i之间的最小price,存在_min[i]里
                _min[i]=pri[i];
            else
                _min[i]=min(pri[i],_min[i-1]);
        }
        for(int i=n;i>=1;i--)  //得到i-n之间的最大price,存在_max[i]里
        {
            if(i==n)
                _max[i]=pri[i];
            else
                _max[i]=max(pri[i],_max[i+1]);
        }
        for(int i=1;i<=n;i++)  //用fgo[i]表示在1-i段最大化的利润
        {
            fgo[i]=max(fgo[i-1],pri[i]-_min[i]);
        }
        for(int i=n;i>=1;i--)  //用fback[i]表示在i-n段最大化的利润

        {
            fback[i]=max(fback[i+1],_max[i]-pri[i]);
        }
        int ans =fgo[1]+fback[2];  //用ans记录两组买卖的最优利润
        for(int i=1;i<=n;i++)
        {
            if(fgo[i]+fback[i+1]>ans
                ans=fgo[i]+fback[i+1];
        }
        cout<<ans<<endl;
    }
    return 0;
}


代码截图

在这里插入图片描述
在这里插入图片描述

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值