1. 程式人生 > >CCF201412-3 集合競價(100分)

CCF201412-3 集合競價(100分)

試題編號: 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的賣單的總股數之間的較小值。
  你的程式需要確定一個開盤價,使得開盤成交量儘可能地大。如果有多個符合條件的開盤價,你的程式應當輸出最高的那一個。 輸入格式   輸入資料有任意多行,每一行是一條記錄。保證輸入合法。股數為不超過108的正整數,出價為精確到恰好小數點後兩位的正實數,且不超過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。

問題描述:(參見上文)。

問題分析:這是一個競價匹配的問題。資料可以儲存在結構陣列中,但未必是好的方案。使用兩個優先佇列,一個用於儲存購入訂單,按價格從大到小排列;另外一個用於儲存賣出訂單,按價格從小到大排列;然後進行價格的匹配處理。

程式說明:(略)。

提交後得100分的C++語言程式如下:

/* CCF201412-3 集合競價 */

#include <iostream>
#include <queue>
#include <cstring>
#include <cstdio>

using namespace std;

const int N = 5000;

struct trading {
    int orderno;
    char t;
    float price;
    long long quantity;
    bool operator < (const trading& n) const {
        if(t == 's')
            return price > n.price;
        else // t == 'b'
            return price < n.price;
    }
};

bool cancelflag[N+1];

int main()
{
    trading t;
    priority_queue<trading> sell, buy;
    string strading;

    // 變數初始化
    memset(cancelflag, false, sizeof(cancelflag));

    // 輸入資料
    int no = 0, tno;

    while(cin >> strading) {
        if(strading[0] == 'c') {
            // 設定交易號
            no++;

            // 輸入取消的交易號
            cin >> tno;

            // 設定取消標誌
            cancelflag[tno] = true;
        } else if(strading[0] == 'b' || strading[0] == 's') {
            // 設定交易號
            t.orderno = ++no;

            // 輸入交易價格和數量
            cin >> t.price >> t.quantity;

            // 將交易分別放入買入和賣出的優先佇列
            if(strading[0] == 'b') {
                t.t = strading[0];
                buy.push(t);
            } else {    // t.trading[0] == 's'
                t.t = strading[0];
                sell.push(t);
            }
        } else
            break;
    }

    // 集合競價處理
    t.price = 0;
    t.quantity = 0;
    trading b, s;
    for(;;) {
        // 清除被取消的訂單(同時將隊頭放在b和s中)
        while(!buy.empty()) {
            b = buy.top();
            if(cancelflag[b.orderno])
                buy.pop();
            else
                break;
        }
        while(!sell.empty()) {
            s = sell.top();
            if(cancelflag[s.orderno])
                sell.pop();
            else
                break;
        }

        // 買賣佇列只要有一個為空,則處理結束
        if(buy.empty() || sell.empty())
            break;

        // 集合競價處理
        if(b.price >= s.price) {
            t.quantity += min(b.quantity, s.quantity);
            t.price = b.price;

            if(b.quantity == s.quantity) {
                buy.pop();
                sell.pop();
            } else if(b.quantity > s.quantity) {
                b.quantity -= s.quantity;
                buy.pop();
                buy.push(b);
                sell.pop();
            } else {    // b.quantity < s.quantity
                buy.pop();
                s.quantity -= b.quantity;
                sell.pop();
                sell.push(s);
            }
        } else
            break;
    }

    // 輸出結果
    printf("%.2f", t.price);
    cout << " " << t.quantity << endl;

    return 0;
}

/*
測試樣例:

sell 8.88 100
sell 8.88 175
sell 9.00 400
buy 8.88 400
cancel 1
sell 100.00 50
8.88 175

buy 9.25 100
buy 8.88 175
buy 9.00 400
sell 8.88 400
cancel 1
buy 100.00 50
9.00 400

buy 9.25 100
buy 8.88 175
buy 9.00 400
sell 8.79 1501
cancel 1
cancel 2
9.00 400

buy 9.25 110
buy 8.88 300
buy 18.88 200
sell 8.88 201
sell 9.25 100
9.25 301
*/