题目链接
题目描述
最近越来越多的人都投身股市,阿福也有点心动了。谨记着“股市有风险,入市需谨慎”,阿福决定先来研究一下简化版的股票买卖问题。
假设阿福已经准确预测出了某只股票在未来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;
}
代码截图