该程序的输入由很多行构成,每一行为一条记录,记录可能有以下几种:
1. buy p s 表示一个购买股票的买单,每手出价为p,购买股数为s。
2. sell p s 表示一个出售股票的卖单,每手出价为p,出售股数为s。
3. cancel i表示撤销第i行的记录。
如果开盘价为p 0,则系统可以将所有出价至少为p 0的买单和所有出价至多为p 0的卖单进行匹配。因此,此时的开盘成交量为出价至少为p 0的买单的总股数和所有出价至多为p 0的卖单的总股数之间的较小值。
你的程序需要确定一个开盘价,使得开盘成交量尽可能地大。如果有多个符合条件的开盘价,你的程序应当输出最高的那一个。
buy 8.88 175
sell 9.00 1000
buy 9.00 400
sell 8.92 400
cancel 1
buy 100.00 50
————————————————————————————————————
题目分析
严重吐槽一下,这题cancel能不能取消另一行的cancel存在很大争议。看到有博主说可以,按这个思路改怎么也取消不了。改为不能取消就100分了。CCF这种问题毫无意义。
这题拿分简单,关键是要弄懂隐含的规则,否则拿满分不易。隐含的规则就是最终输出的开盘价一定是输入的p中。为什么呢,将测试案例按p从小到大排列开,如下图所示。图示中各价格用序号标识好,方便说明。
(下述最优解意思是可以取到最大开盘价的解)
假设(8.92,9.00)区间(注意是开区间)内某一点p是最优价格,那么大于或等于这个价格的buy点有3、5,小于等于它的sell点有2,那么不管p在区间(8.92,9.00)内怎么浮动,都不影响大于它的sell点和小于它的buy点的选择,都是点2和点3、5。题目要求取最大值,所以在假设在(2,3)区间存在最优价格的情况下,取值可以无限逼近与9.00。
当价格9.00存在sell点时,值取到9.00显然不会改变原有的buy、sell点的选择。
当9.00存在sell点时,取9.00将会影响buy、sell点的选择。在趋于9.00时,设sell的总股数为a,buy的总股数为b。取到9.00时.设sell的总股数为a+c,buy的总股数为b,很容易明白min(a,b) <= min(a+c,b),即在9.00存在sell点时,得到的结果总会优于趋近于9.00却没有取9.00上的sell点的情况。推广开来,我们可以知道,假设两个点的区间(a,b]上存在最优解,那么b一定也是最优解,且b是最大的,所以b是答案。所以答案一定是存在于已知的点上 。
有人可能还会有疑问这,道题趋近于9和取就是两种截然不同的,取就的话sell就可以取点4,是不是说明有可能存在某种可能,取了点4结果不是最优的,而不取点9.00却趋近于点9.00(比如价格为8.99)价格是最优的?我们上面的证明得出以下结论:
S:两个已知点a,b构成的区间(a,b)存在最优解 B:b是最优解
在已知条件S的情况下可以推出结论B。即S -> B
但是对于S的否命题 S:两个已知点a,b构成的区间(a,b)不存在最优解。是不能得出b是最优解的。即S -> B是错误的。
所以区间(a,b)存不存在最优解和b是最优解没有直接关系,但是(a,b)存在最优解b就一定是最优解。
————————————————————————————————————————————————
算法解释
既然已知开盘价一定是输入的价格中的一个,那么将所有价格从小到大排开,循环依次检测每个价格,比较出最小的值就行。要注意的是每次判断一个价格选择时的成交价,我们不必从头到尾计算一次该价格下的成交价,这样太费时间,对于本体测试数据有以下优化算法:
- sum1用于计算buy买单总股数,sum2用于计算sell卖单总股数。初始化sum1为所有的buy点的和。测试数据中为625,sum1=625。sum2=0。flag=0
- 首先检测最小的价格8.88时,该点为buy点,标记flag为1。sum1,sum2不变。取sum1和sum2中的最小值。
- 到第2个点8.92时,由于flag==1,sum1减去上一个buy的值,并把flag置0,即sum1 = sum1- 175。由于该点是sell点,sum2 = sum2 + 400。比较sum1和sum2。
- 到到9.00时,检测到该点既有sell点也有buy点,则flag=1,sum2=sum2+1000。比较sum1,sum2。
- 后面重复上面步骤到经过所有点。注意,如果存在多个buy点价格一样,或者多个sell点价格一样,一定要合并相同点,即把所有的股数累加到一个点上并删除其他相同的点。否则会重复计算。
#include <iostream>
#include <vector>
#include <iomanip>
#include <algorithm>
using namespace std;
struct ins
{
int count;
int flag;
double price;
long long num;
int use_flag;
};
vector <struct ins> inst,in1,in2;
bool cmp(struct ins a,struct ins b)
{
if(a.price < b.price)
{
return true;
}
else
{
return false;
}
}
int main()
{
long long temp,num1=0,num2=0,flag=0;
string s0;
double p,PP;
int i,j;
long long n,sum1,sum2,SSUM=0;
struct ins po;
while(cin >> s0)
{
if(s0=="buy")
{
cin >> p >> n;
po.flag = 0;
po.price = p;
po.num = n;
po.use_flag = 0;
inst.push_back(po);
}
else if(s0=="sell")
{
cin >> p >> n;
po.flag = 1;
po.price = p;
po.num = n;
po.use_flag = 0;
inst.push_back(po);
}
else if(s0=="cancel")
{
cin >> n;
po.flag = 2;
po.num = n;
po.use_flag = 0;
inst.push_back(po);
}
}
//标记需要删除
for(i=0;i<inst.size();i++)
{
//本来设置的是cancel可以取消另一行的cancel,结果只有50分,改为不允许取消就有100分了
if(inst[i].flag == 2 )
{
j = inst[i].num;
inst[j-1].use_flag = 1;
}
}
for(i=0;i<inst.size();)
{
if( inst[i].flag == 2 || inst[i].use_flag == 1)
{
//删除cancel和所有被cancel删除的
inst.erase(inst.begin()+i);
}else{
if(inst[i].flag == 0) //将buy插入in1
{
in1.push_back(inst[i]);
}else{ //将sell插入in2
in2.push_back(inst[i]);
}
i++;
}
}
//从小到大排序
sort(in1.begin(),in1.end(),cmp);
sort(in2.begin(),in2.end(),cmp);
sort(inst.begin(),inst.end(),cmp);
//删除相同价格的
for(i=0;i<inst.size()-1;)
{
if(inst[i].price == inst[i+1].price)
{
inst.erase(inst.begin()+i);
}else
{
i++;
}
}
sum1 = 0;
sum2 = 0;
//合并相同价格的buy点
for(i=0;i<in1.size()-1;)
{
if(in1[i].price == in1[i+1].price)
{
in1[i].num += in1[i+1].num;
in1.erase(in1.begin()+i+1);
}else{
i++;
}
}
//合并相同价格的sell点
for(i=0;i<in2.size()-1;)
{
if(in2[i].price == in2[i+1].price)
{
in2[i].num += in2[i+1].num;
in2.erase(in2.begin()+i+1);
}else{
i++;
}
}
//统计所有buy点的和
for(i=0;i<in1.size();i++)
{
sum1 += in1[i].num;
}
for(i=0;i<inst.size();i++)
{
p = inst[i].price;
if(flag == 1)
{
flag = 0;
sum1 = sum1 - in1[num1-1].num;
}
if( in1[num1].price == p )
{
num1++;
flag=1;
}
if(in2[num2].price == p)
{
sum2 = sum2 + in2[num2++].num;
}
if(sum1 > sum2)
{
temp = sum2;
}else
{
temp = sum1;
}
if(temp >= SSUM)
{
PP=p;
SSUM=temp;
}
}
cout << setiosflags(ios::fixed) << setprecision(2) << PP << " " << SSUM;
return 0;
}