hihoCoder 1401 Registration

多队列模拟. 与POJ #1025 Department类似, 不过简化很多. 貌似这类模拟题经常出现. 用STL中的优先队列 (priority_queue<>) 很好写.


这题我写得很不顺, 老年选手退步太快, 记录一下我犯的一个很隐蔽的错误, 从前对此毫无认识, 想想都可怕, 太菜了.

这道题优先队列里维护的事件 (event) 是等待.
释放这些等待的先后顺序如下:

在同一队列 (此"队列"指某个office门前的队列, 与"优先队列"中的"队列"含义不同;下同) 中的两个事件, 按(开始等待时刻, 学号)这两个关键字排序. 在不同队列中的两事件按开始等待的时刻排序.

思路是比较容易想到的. 但我的实现里有个很subtle的错误.

Wrong implementation

#include <bits/stdc++.h>
using namespace std;

const int N{1<<14}, M{1<<7};
int s[N], res[N];
vector<pair<int,int>> r[N];   // registration
int avail[M];
int n, m, k;

struct W{
    int id, beg, idx;
    int des()const{
        return r[id][idx].first;
    }
    int dur()const{
        return r[id][idx].second;
    }
    int num()const{
        return s[id];
    }
    int end()const{
        return max(beg, avail[des()])+dur();    // error-prone
    }
    W next(){
        return {id, end()+k, idx+1};
    }
    void out(){
        cout<<id<<' '<<beg<<' '<<idx<<' '<<avail[des()]<<endl;
    }
};

// greater<_Tp> is a function object
// constexpr std::greater<_Tp>::operator()(const _Tp &, const_Tp &)const

bool cmp(const W &a, const W &b){  //! > must be a const
    if(a.des()!=b.des()){
        return a.beg>b.beg;
    }
    else{
        return a.beg!=b.beg?a.beg>b.beg:a.num()>b.num();
    }
}


priority_queue<W, vector<W>, decltype(cmp)*> que(cmp);

int main(){
    cin>>n>>m>>k;
    for(int i=0; i<n; i++){
        int q, t;
        cin>>s[i]>>t>>q;
        for(; q--; ){
            int o, t;
            cin>>o>>t;
            r[i].push_back({o, t});
        }
        que.push({i, t+k, 0});
    }

    for(; !que.empty(); ){
        W top=que.top();
        que.pop();
        if(top.idx<r[top.id].size()-1){
            que.push(top.next());
        }
        else{
            res[top.id]=top.end();
        }
        avail[top.des()]=top.end();
    }
    for(int i=0; i<n; i++)
        cout<<res[i]<<endl;
    return 0;
}

注意其中的比较函数cmp:

bool cmp(const W &a, const W &b){  //! > must be a const
    if(a.des()!=b.des()){
        return a.beg>b.beg;
    }
    else{
        return a.beg!=b.beg?a.beg>b.beg:a.num()>b.num();
    }
}

首先必须认识到: 优先队列要求元素的键值 (key) 在所定义的比较函数下是全序的 (totally ordered).

If \(X\) is totally ordered under \(\le\), then the following statements hold for all \(a, b\) and \(c\) in \(X\):
if \(a \le b\) and \(b \le a\) then $ a=b$ (antisymmetry);
if \(a \le b\) and \(b\le c\) then \(a \le c\) (transitivity);
\(a \le b\) or \(b \le a\) (totality).

我所定义的比较函数并不是全序的. 问题中的集合\(X\)内的元素是一个三元组\((id, des, time)\). 我们定义的比较函数cmp是一个strict weak ordering (<). 这个binary relation应当满足

if \(a < b\) and \(b<c\), then \(a<c\)
\(a< b\) or $ b<a$ or \(a=b\).

但是据此会导出矛盾, 考虑

\(a =(id_1, des_1, t), b=(id_2, des_1, t), c=(id_3, des_2, t), id_1 < id_2 \Longrightarrow a > b, a=c, b=c\).
矛盾!

因而这个比较函数不是全序的.
但是可以略微改一下:

\(time\)为第一关键字, \(des\)为第二关键字, \(id\)为第三关键字.

这样不但思路更清楚, 代码也可以简化

Implementation

#include <bits/stdc++.h>
using namespace std;

const int N{1<<14}, M{1<<7};
int s[N], res[N];
vector<pair<int,int>> r[N];   // registration
int avail[M];
int n, m, k;

struct W{
    int id, beg, idx;
    int des()const{
        return r[id][idx].first;
    }
    int dur()const{
        return r[id][idx].second;
    }
    auto make_tuple()const{
        return std::make_tuple(beg, des(), num());
    }
    int num()const{
        return s[id];
    }
    bool operator<(const W &rhs)const{
        return make_tuple() > rhs.make_tuple();
    }
    int end()const{
        return max(beg, avail[des()])+dur();    // error-prone
    }
    W next(){
        return {id, end()+k, idx+1};
    }
    void out(){
        cout<<id<<' '<<beg<<' '<<idx<<' '<<avail[des()]<<endl;
    }
};

priority_queue<W> que;

int main(){
    cin>>n>>m>>k;
    for(int i=0; i<n; i++){
        int q, t;
        cin>>s[i]>>t>>q;
        for(; q--; ){
            int o, t;
            cin>>o>>t;
            r[i].push_back({o, t});
        }
        que.push({i, t+k, 0});
    }

    for(; !que.empty(); ){
        W top=que.top();
        que.pop();
        if(top.idx<r[top.id].size()-1){
            que.push(top.next());
        }
        else{
            res[top.id]=top.end();
        }
        avail[top.des()]=top.end();
    }
    for(int i=0; i<n; i++)
        cout<<res[i]<<endl;
    return 0;
}
posted @ 2016-11-03 21:56  Pat  阅读(168)  评论(0编辑  收藏  举报