UVa 1598 Exchange

题目描述

你正在参与一个大型项目,用于自动化东北资源和商品交易所(NEERC\texttt{NEERC}NEERC)的操作。各种资源和商品通过公开拍卖在该交易所进行交易。每种资源或商品都是独立交易的,你的任务是为该交易所编写一个核心引擎——订单簿。

订单簿接收一个消息流,包括三种类型的消息:

  • BUY\texttt{BUY}BUY:买入订单,后跟数量 qqq 和价格 ppp
  • SELL\texttt{SELL}SELL:卖出订单,后跟数量 qqq 和价格 ppp
  • CANCEL\texttt{CANCEL}CANCEL:取消订单,后跟要取消的订单编号 iii

订单簿需要维护活跃订单列表,并在每次消息处理后:

  1. 输出所有发生的交易(TRADE\texttt{TRADE}TRADE
  2. 输出当前报价(QUOTE\texttt{QUOTE}QUOTE

题目分析

核心机制

  1. 订单匹配

    • 当买入订单价格 ≥ 当前最低卖出价时,发生交易
    • 当卖出订单价格 ≤ 当前最高买入价时,发生交易
    • 相同价格的订单按 FIFO\texttt{FIFO}FIFO(先进先出)顺序匹配
  2. 报价计算

    • 买入价(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):所有最低卖出价订单的总数量
  3. 特殊情况

    • 无买入订单:买入数量为 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> 记录每个订单是否活跃
关键算法流程
  1. 处理买入订单

    • 检查当前最低卖出价是否 ≤≤ 订单价格
    • 如果是,按 FIFO\texttt{FIFO}FIFO 顺序与卖出订单匹配,生成交易
    • 匹配完成后,如果还有剩余数量,添加到买入订单簿
  2. 处理卖出订单

    • 检查当前最高买入价是否 ≥≥ 订单价格
    • 如果是,按 FIFO\texttt{FIFO}FIFO 顺序与买入订单匹配,生成交易
    • 匹配完成后,如果还有剩余数量,添加到卖出订单簿
  3. 处理取消订单

    • 简单将对应订单标记为非活跃状态
  4. 计算报价

    • 遍历买入订单找到最高价且数量不为零的价位
    • 遍历卖出订单找到最低价且数量不为零的价位
    • 处理特殊情况(无订单时)

复杂度分析

  • 时间复杂度:每个订单处理最坏情况 O(n)O(n)O(n),总体 O(n2)O(n^2)O(n2),在 n≤10000n \leq 10000n10000 时可接受
  • 空间复杂度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}mapvector\texttt{vector}vector 的组合,我们能够高效地处理订单的添加、匹配和取消操作,并在每次消息处理后正确输出交易信息和市场报价。

关键点在于理解订单匹配的优先级规则(价格优先,同价格时间优先)和特殊情况处理(无订单时的默认值),这些都是金融交易系统中常见的设计模式。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值