题目描述
你正在参与一个大型项目,用于自动化东北资源和商品交易所(NEERC\texttt{NEERC}NEERC)的操作。各种资源和商品通过公开拍卖在该交易所进行交易。每种资源或商品都是独立交易的,你的任务是为该交易所编写一个核心引擎——订单簿。
订单簿接收一个消息流,包括三种类型的消息:
- BUY\texttt{BUY}BUY:买入订单,后跟数量 qqq 和价格 ppp
- SELL\texttt{SELL}SELL:卖出订单,后跟数量 qqq 和价格 ppp
- CANCEL\texttt{CANCEL}CANCEL:取消订单,后跟要取消的订单编号 iii
订单簿需要维护活跃订单列表,并在每次消息处理后:
- 输出所有发生的交易(TRADE\texttt{TRADE}TRADE)
- 输出当前报价(QUOTE\texttt{QUOTE}QUOTE)
题目分析
核心机制
-
订单匹配:
- 当买入订单价格 ≥ 当前最低卖出价时,发生交易
- 当卖出订单价格 ≤ 当前最高买入价时,发生交易
- 相同价格的订单按 FIFO\texttt{FIFO}FIFO(先进先出)顺序匹配
-
报价计算:
- 买入价(bid price\texttt{bid price}bid price):最高买入订单的价格
- 卖出价(ask price\texttt{ask price}ask price):最低卖出订单的价格
- 买入数量(bid size\texttt{bid size}bid size):所有最高买入价订单的总数量
- 卖出数量(ask size\texttt{ask size}ask size):所有最低卖出价订单的总数量
-
特殊情况:
- 无买入订单:买入数量为 000,买入价为 000
- 无卖出订单:卖出数量为 000,卖出价为 999999999999999
算法设计思路
数据结构选择
- 买入订单:使用 map<int, vector<int>, greater<int>>\texttt{map<int, vector<int>, greater<int>>}map<int, vector<int>, greater<int>>,键为价格(降序),值为订单索引列表
- 卖出订单:使用 map<int, vector<int>>\texttt{map<int, vector<int>>}map<int, vector<int>>,键为价格(升序),值为订单索引列表
- 订单存储:使用 vector<Order>\texttt{vector<Order>}vector<Order> 存储所有订单信息
- 活跃状态:使用 vector<bool>\texttt{vector<bool>}vector<bool> 记录每个订单是否活跃
关键算法流程
-
处理买入订单:
- 检查当前最低卖出价是否 ≤≤≤ 订单价格
- 如果是,按 FIFO\texttt{FIFO}FIFO 顺序与卖出订单匹配,生成交易
- 匹配完成后,如果还有剩余数量,添加到买入订单簿
-
处理卖出订单:
- 检查当前最高买入价是否 ≥≥≥ 订单价格
- 如果是,按 FIFO\texttt{FIFO}FIFO 顺序与买入订单匹配,生成交易
- 匹配完成后,如果还有剩余数量,添加到卖出订单簿
-
处理取消订单:
- 简单将对应订单标记为非活跃状态
-
计算报价:
- 遍历买入订单找到最高价且数量不为零的价位
- 遍历卖出订单找到最低价且数量不为零的价位
- 处理特殊情况(无订单时)
复杂度分析
- 时间复杂度:每个订单处理最坏情况 O(n)O(n)O(n),总体 O(n2)O(n^2)O(n2),在 n≤10000n \leq 10000n≤10000 时可接受
- 空间复杂度:O(n)O(n)O(n),存储所有订单信息
代码实现
// Exchange
// UVa ID: 1598
// Verdict: Accepted
// Submission Date: 2025-11-22
// UVa Run Time: 1.530s
//
// 版权所有(C)2025,邱秋。metaphysis # yeah dot net
#include <bits/stdc++.h>
using namespace std;
// 订单结构体
struct Order {
int size; // 订单数量
int price; // 订单价格
bool isBuy; // 是否为买入订单
// 构造函数,提供默认参数以便vector resize使用
Order(int s = 0, int p = 0, bool buy = false) : size(s), price(p), isBuy(buy) {}
};
// 订单簿类,封装所有订单簿操作
struct OrderBook {
// 买入订单:价格降序排列,每个价格对应订单索引列表
map<int, vector<int>, greater<int>> buyOrders;
// 卖出订单:价格升序排列,每个价格对应订单索引列表
map<int, vector<int>> sellOrders;
// 存储所有订单
vector<Order> allOrders;
// 记录每个订单是否活跃
vector<bool> active;
// 初始化订单簿
void init(int n) {
buyOrders.clear();
sellOrders.clear();
allOrders.resize(n + 1); // 索引从1开始
active.assign(n + 1, false);
}
// 输出当前报价
void printQuote() {
int bidSize = 0, bidPrice = 0, askSize = 0, askPrice = 99999;
// 查找最佳买入价和数量
for (auto& pair : buyOrders) {
int price = pair.first;
int totalSize = 0;
// 统计该价格下所有活跃订单的总数量
for (int orderId : pair.second) {
if (active[orderId]) {
totalSize += allOrders[orderId].size;
}
}
if (totalSize > 0) {
bidSize = totalSize;
bidPrice = price;
break; // 找到最高价就退出
}
}
// 查找最佳卖出价和数量
for (auto& pair : sellOrders) {
int price = pair.first;
int totalSize = 0;
// 统计该价格下所有活跃订单的总数量
for (int orderId : pair.second) {
if (active[orderId]) {
totalSize += allOrders[orderId].size;
}
}
if (totalSize > 0) {
askSize = totalSize;
askPrice = price;
break; // 找到最低价就退出
}
}
// 输出报价
cout << "QUOTE " << bidSize << " " << bidPrice << " - " << askSize << " " << askPrice << endl;
}
// 处理买入订单
void processBuy(int orderId) {
Order& order = allOrders[orderId]; // 获取订单引用
// 当订单还有数量且存在可匹配的卖出订单时循环
while (order.size > 0 && !sellOrders.empty()) {
auto it = sellOrders.begin(); // 获取最低卖出价
int bestAsk = it->first;
// 如果最低卖出价高于买入订单价格,无法匹配
if (bestAsk > order.price) break;
auto& orderList = it->second; // 该价格下的订单列表
// 遍历该价格下的所有订单(FIFO顺序)
for (int i = 0; i < orderList.size() && order.size > 0; ) {
int sellOrderId = orderList[i];
// 跳过非活跃订单
if (!active[sellOrderId]) {
orderList.erase(orderList.begin() + i);
continue;
}
Order& sellOrder = allOrders[sellOrderId];
// 计算交易数量(取两者较小值)
int tradeSize = min(order.size, sellOrder.size);
// 输出交易信息
cout << "TRADE " << tradeSize << " " << bestAsk << endl;
// 更新订单数量
order.size -= tradeSize;
sellOrder.size -= tradeSize;
// 如果卖出订单数量归零,标记为非活跃并移除
if (sellOrder.size == 0) {
active[sellOrderId] = false;
orderList.erase(orderList.begin() + i);
} else {
i++; // 移动到下一个订单
break; // 部分成交后退出当前价格级别的匹配
}
}
// 如果该价格下没有活跃订单了,从订单簿中移除
if (orderList.empty()) {
sellOrders.erase(it);
}
}
// 如果订单还有剩余数量,添加到买入订单簿
if (order.size > 0) {
buyOrders[order.price].push_back(orderId);
active[orderId] = true;
}
}
// 处理卖出订单(逻辑与买入对称)
void processSell(int orderId) {
Order& order = allOrders[orderId];
while (order.size > 0 && !buyOrders.empty()) {
auto it = buyOrders.begin(); // 获取最高买入价
int bestBid = it->first;
// 如果最高买入价低于卖出订单价格,无法匹配
if (bestBid < order.price) break;
auto& orderList = it->second;
for (int i = 0; i < orderList.size() && order.size > 0; ) {
int buyOrderId = orderList[i];
if (!active[buyOrderId]) {
orderList.erase(orderList.begin() + i);
continue;
}
Order& buyOrder = allOrders[buyOrderId];
int tradeSize = min(order.size, buyOrder.size);
cout << "TRADE " << tradeSize << " " << bestBid << endl;
order.size -= tradeSize;
buyOrder.size -= tradeSize;
if (buyOrder.size == 0) {
active[buyOrderId] = false;
orderList.erase(orderList.begin() + i);
} else {
i++;
break;
}
}
if (orderList.empty()) {
buyOrders.erase(it);
}
}
if (order.size > 0) {
sellOrders[order.price].push_back(orderId);
active[orderId] = true;
}
}
// 处理取消订单请求
void processCancel(int orderId) {
// 检查订单编号有效性且订单当前活跃
if (orderId >= 1 && orderId < allOrders.size() && active[orderId]) {
active[orderId] = false; // 标记为非活跃
}
}
};
int main() {
int n;
bool firstCase = true; // 标记是否为第一个测试用例
OrderBook book; // 订单簿实例
// 循环处理每个测试用例
while (cin >> n) {
// 测试用例间输出空行(第一个除外)
if (!firstCase) cout << endl;
firstCase = false;
// 初始化订单簿
book.init(n);
// 处理n条消息
for (int i = 1; i <= n; i++) {
string type;
cin >> type;
if (type == "BUY") {
int q, p;
cin >> q >> p;
// 创建买入订单并处理
book.allOrders[i] = Order(q, p, true);
book.processBuy(i);
} else if (type == "SELL") {
int q, p;
cin >> q >> p;
// 创建卖出订单并处理
book.allOrders[i] = Order(q, p, false);
book.processSell(i);
} else if (type == "CANCEL") {
int id;
cin >> id;
// 处理取消订单请求
book.processCancel(id);
}
// 每次消息处理后输出当前报价
book.printQuote();
}
}
return 0;
}
总结
本题实现了一个完整的订单簿系统,核心在于正确维护买入和卖出订单的数据结构,以及实现符合 FIFO\texttt{FIFO}FIFO 规则的订单匹配算法。通过合理使用 map\texttt{map}map 和 vector\texttt{vector}vector 的组合,我们能够高效地处理订单的添加、匹配和取消操作,并在每次消息处理后正确输出交易信息和市场报价。
关键点在于理解订单匹配的优先级规则(价格优先,同价格时间优先)和特殊情况处理(无订单时的默认值),这些都是金融交易系统中常见的设计模式。


1280

被折叠的 条评论
为什么被折叠?



