ccf-201412-3 集合竞价(详解)
试题编号: 201412-3
试题名称: 集合竞价
时间限制: 1.0s
内存限制: 256.0MB
问题描述:
问题描述
某股票交易所请你编写一个程序,根据开盘前客户提交的订单来确定某特定股票的开盘价和开盘成交量。
该程序的输入由很多行构成,每一行为一条记录,记录可能有以下几种:
1. buy p s 表示一个购买股票的买单,每手出价为p,购买股数为s。
2. sell p s 表示一个出售股票的卖单,每手出价为p,出售股数为s。
3. cancel i表示撤销第i行的记录。
如果开盘价为p0,则系统可以将所有出价至少为p0的买单和所有出价至多为p0的卖单进行匹配。因此,此时的开盘成交量为出价至少为p0的买单的总股数和所有出价至多为p0的卖单的总股数之间的较小值。
你的程序需要确定一个开盘价,使得开盘成交量尽可能地大。如果有多个符合条件的开盘价,你的程序应当输出最高的那一个。
输入格式
输入数据有任意多行,每一行是一条记录。保证输入合法。股数为不超过10^8的正整数,出价为精确到恰好小数点后两位的正实数,且不超过10000.00。
输出格式
你需要输出一行,包含两个数,以一个空格分隔。第一个数是开盘价,第二个是此开盘价下的成交量。开盘价需要精确到小数点后恰好两位。
样例输入
buy 9.25 100
buy 8.88 175
sell 9.00 1000
buy 9.00 400
sell 8.92 400
cancel 1
buy 100.00 50
样例输出
9.00 450
评测用例规模与约定
对于100%的数据,输入的行数不超过5000。
code
#include <iostream>
#include <string>
#include <vector>
#include <algorithm>
using namespace std;
struct data {
string a;
float b;
long long c;
int d;
};
struct money {
float a;
long long b,s;
};
vector<data> notes;
vector<money> mon;
data temp;
money mtemp;
bool com(data a,data b) {
if(a.d==b.d&&a.d==1) {
return a.b<b.b;
}
return a.d>b.d;
}
void find(float a,string b,long long c) {
int i;
for(i=0; i<mon.size(); i++) {
if(mon[i].a==a) {
if(b=="buy") {
mon[i].b+=c;
} else {
mon[i].s+=c;
}
break;
}
}
if(i==mon.size()) {
mtemp.a=a;
if(b=="buy") {
mtemp.b=c;
mtemp.s=0;
} else {
mtemp.s=c;
mtemp.b=0;
}
mon.push_back(mtemp);
}
}
int main() {
while(cin>>temp.a) {
if(temp.a=="buy"||temp.a=="sell") {
cin>>temp.b>>temp.c;
temp.d=1;
} else if(temp.a=="cancel") {
cin>>temp.b;
notes[temp.b-1].d=0;
temp.d=0;
} else {
break;
}
notes.push_back(temp);
}
sort(notes.begin(),notes.end(),com);
for(int i=notes.size()-1; i>-1; i--) {
if(notes[i].d==1) {
find(notes[i].b,notes[i].a,notes[i].c);
} else {
}
}
long long max=-1;
float order;
for(int i=0; i<mon.size(); i++) {
long long sumb=0,sums=0;
for(int j=0; j<=i; j++) {
sumb+=mon[j].b;
}
for(int j=i; j<mon.size(); j++) {
sums+=mon[j].s;
}
long long m=sumb<sums?sumb:sums;
if(max<m) {
max=m;
order=mon[i].a;
}
}
printf("%.2f %lld\n",order,max);
return 0;
}
思路解析:
关键语句解析:
1)
cancel i表示撤销第i行的记录
“撤销第i行的记录”: 这里的撤销只是撤销记录本身,被撤销记录所造成的影响不会消失!!!比如:
buy 9.00 100
sell 8.88 200
cancel 2
cancel 3
有效的记录是:
buy 9.00 100
其实只要你认为记录是顺序执行的,从第一条执行到最后一行,那么得出的结果就是正确的,和题意相符(2018年9月7号:测试数据里面有大概50%的数据和此有关,剩下50%无所谓)。不过我在考虑的时候,多想了一些,我之前认为撤销不仅仅撤销记录本身,被撤销记录所造成的影响也会消失!比如上面的例子,有效的记录是:
buy 9.00 100
sell 8.88 200
但这是不符合题意的(我坚持认为,这不能怪我,是题目没有解释清楚),这让我想起高中的时候,老师为出题人辩解的话:“不要过度解读题意,要按照出题人的思路来(我认为这是出题人的水平不行,出题不严谨)”,所以我们不要想的太多(但也不能想的太少),不能过度解读!
2)
保证输入合法
这句话非常有用,这表明不会出现以下的不合理情况:
buy 9.00 100
sell 8.88 200
cancel 2
cancel 2
不会撤销已经被撤销的记录!
3)
股数为不超过10^8的正整数
对于100%的数据,输入的行数不超过5000
这两句话一起说,这表明成交量可能会是一个很大的数,int表示不了这么大的数,需要用long long等范围比较大的数(2018年9月7号:测试数据里面有大概20%的数据需要用long long)。
4)
开盘价需要精确到小数点后恰好两位
需要注意输出格式,用:printf("%.2f %lld\n",(1),(2));
比较好,“%.2f”表示float类型精确到小数点后恰好两位,“%lld”表示long long类型!
5)
出价为精确到恰好小数点后两位的正实数,且不超过10000.00
几乎没用,可以忽略!
开盘价分析:
!!!开盘价一定是买价中的一个!!!
证明:
- 如果开盘价即在买价中,又在卖价中。
那开盘价已经是买价之一了。 - 如果开盘价p1只在卖价中。
那么在买价中找到与p1价格相差最小,却又比p1大的价格p2。(若找不到p2,p1比买价中的任意价格都要大,此时成交量为0,无意义,不考虑这种情况)当开盘价为p2时,买价中至少为开盘价的总股数与开盘价为p1时相等,卖价中至少为开盘价的总股数会大于或等于开盘价为p1时。所以当开盘价为p2时,成交量要么与开盘价是p1时相等,要么比p1时大。题目中说了,如果有多个符合条件的开盘价,应输出最大的价格,所以此时开盘价应选为p2,即开盘价不可能只在卖价中。 - 如果开盘价p1既不在买价中又不在卖价中。
我们先来定义一个概念,在买价和卖价中找到与p1价格相差最小,却又比p1大的价格,我称之为M价。那么p1会是M价减去0.01(精确到恰好小数点后两位),M价可能是买家、卖价或买卖价。当M价为买价时,开盘价为p1和开盘价为M价没有区别,买价至少为开盘价的总股数和卖价至少为开盘价的总股数都对应相等。当M价为卖价时或即在买价中又在卖价中,开盘价为M价的买价至少为开盘价的总股数与开盘价为p1是相等的,开盘价为M价的卖价至多为开盘价的总股数大于等于开盘价为p1时。题目中说了,如果有多个符合条件的开盘价,应输出最大的价格,所以此时开盘价应选为M价,这就又到了讨论M价的情况了(1、2、3再走一遍)。
此分析借鉴于https://blog.csdn.net/liujiayu1015/article/details/53518842
代码解析:
1、构建结构体data表示每一条记录,属性有a:储存记录标识,b:出价,c:股数,d:是否失效
2、while循环读取每一行记录,用cin即可,当cin的数据不是“buy”、“sell”、“cancel”时跳出循环,读取的记录存入data。
3、读取“cancel”后,立即处理即可,使第某行失效
4、将data按照出价从小到大排序
5、构建结构体money表示每一个出价(包含买价卖价),属性有a:出价,b:买入股数,s:卖出股数
6、按照data的数据初始化money,出价从大到小
7、money中每一项依次定为开盘价,计算成交量,取成交量最大,出价最高的
data形如:
a——–b——–c——–d
cancel-2——-0——–0
sell—-8.88—-200—–1
buy—-9.00—-100—–1
money形如:
a——–b——–s
9.00—100—–0
8.88—0—–200
这样这道题就解决了!