【LSGDOJ1836】: 量化交易 贪心
题目描述
applepi 训练了一个可以自动在股票市场进行量化交易的模型。通常来说,applepi 写出的模型,你懂得,就好比一架印钞机。不过为了谨慎起见,applepi还是想先检查一下模型的效果。applpie 收集了“塞帕思股份(surpass)”在最近的连续 N 天内的价格。在每一天中,他可以做如下事情之一:
1. 睡(把)觉(妹)。
2. 以当天的价格作为成交价买入 1 股“塞帕思”的股票。
3. 以当天的价格作为成交价卖出 1 股“塞帕思”的股票。
最初 applepi 不持有该股票。现在你需要计算出在最优策略下,N 天后 applepi能够获得的最大利润。为了维护森林的和平,本着清仓甩锅的原则,在 N 天的交易结束后 applepi 也不能持有“塞帕思”的股票。
输入
每个测试点包含若干组数据。对于每组数据:
第一行 1 个整数 N。
第二行 N 个正整数,相邻两个整数之间用 1 个空格隔开,表示每一天股票的价格。
输出
对于每组数据,首先按样例所示的格式“Case #k:”输出该组数据的编号,然后输出一个整数,表示 applepi 最大能够获得的利润。
样例输入
6 2 6 7 3 5 6 8 1 2 3 4 5 6 7 8
样例输出
Case #1: 8
Case #2: 16
提示
样例输入 2
10
15831 47573 60015 51368 32460 34125 43074 75172 54014 93578
样例输出 2
Case #1: 161084
对于 50%的数据,1≤N≤1000。
对于 100%的数据,1≤N≤100000,股票价格不超过 100000,每个测试点至多包含 5 组数据。
题解:
这题正解好巧妙:
是个人都知道:如果当前已经买了一股,那么就要卖到最大的一天.
->但是我们并不知道那一天最大.
->于是我们在能产生利润时就卖掉.
->但是我们需要一个“后悔”操作,表示那天不卖,在此时利润更大时再买.
于是开个小根堆表示已经买了的股票.
当输入小于堆顶说明产生不了利益,先买掉
当输入大于堆顶说明可以产生利润,于是卖掉,并且把当天股价加入堆中两次(可以达到“后悔”的效果)
下面是”后悔“操作的原理:
假设堆顶是第a天的报价,当前是第b的的报价,第a天买进的股票应该在第c天卖出,
第b天买进的股票需要在第d天卖出,
a<b<c<d,收益p[c]-p[a]+p[d]-p[b]
现在把第a天的股票在第b天抛出,p[b]-p[a],然后又买进第b天的股票,那么在第c天的时候,
堆顶为第b天的股票,抛出第b天的股票 p[c]-p[b],收益总和p[b]-p[a]+p[c]-p[b]=p[c]-p[a]
但因为第b天的股票应该在第d天抛出,所以要第二次再次买进第b天的股票
下面来分析具体含义:
1.假如一个元素被取出一次,说明当天既没买也没卖(睡觉)
2.假如取出了两次,说明确实是卖掉了.
3.加入一个元素没有被取出过,相当于在当天买了.
为什么答案满足最大:
1.买入的都是没有被取出过的,因为是小根堆,所以满足最大.
2.同理卖出的都是最小的.
3.又因为是按i=1-n处理的所以满足买卖顺序.
下面是代码:
1 #include<iostream> 2 #include<cstdio> 3 #include<algorithm> 4 #include<cstring> 5 #include<cmath> 6 #include<queue> 7 #include<vector> 8 using namespace std; 9 int n; 10 int gi(){ 11 int str=0;char ch=getchar(); 12 while(ch>'9' || ch<'0')ch=getchar(); 13 while(ch>='0' && ch<='9')str=str*10+ch-'0',ch=getchar(); 14 return str; 15 } 16 int cnt=0; 17 int t[400000],num=0; 18 void putin(int x) 19 { 20 t[++num]=x; 21 int now=num,next; 22 while(now>1) 23 { 24 next=(now>>1); 25 if(t[now]>=t[next])break; 26 swap(t[now],t[next]); 27 now=next; 28 } 29 } 30 31 int getit() 32 { 33 int str=t[1]; 34 t[1]=t[num--]; 35 int now=1,next; 36 while((now<<1)<=num) 37 { 38 next=now<<1; 39 if(t[next]>t[next+1] && next<num)next++; 40 if(t[now]<=t[next])break; 41 swap(t[now],t[next]); 42 now=next; 43 } 44 return str; 45 } 46 47 void work() 48 { 49 cnt++; 50 int x,tmp;long long ans=0; 51 for(int i=1;i<=n;i++) 52 { 53 x=gi(); 54 if(!num || t[1]>=x)putin(x); 55 else 56 { 57 tmp=getit(); 58 ans+=x-tmp; 59 putin(x); 60 putin(x); 61 } 62 } 63 printf("Case #%d: %lld\n",cnt,ans); 64 } 65 int main() 66 { 67 while(~scanf("%d",&n)){ 68 work(); 69 num=0; 70 } 71 }