暑期集训20190729 字典序(dictionary)

【题目描述】 你需要构造一个1~n的排列,使得它满足m个条件,每个条件形如(ai,bi),表示ai必须在bi前面。 在此基础上,你需要让1尽可能靠前,然后你需要让2尽可能靠前,然后是3,4,5,…,n。

【输入数据】 第一行两个正整数n,m。接下来m行每行两个数ai,bi。

【输出数据】 输出一行n个整数表示答案。如果不存在这样的排列,输出-1。

【样例输入】 5 4 5 4 5 3 4 2 3 2

【样例输出】 1 5 3 4 2

【数据范围】 对于20%的数据,n,m<=10。 对于40%的数据,n,m<=200。 对于60%的数据,n,m<=1000。 对于100%的数据,n,m<=100000。

对于每组条件连一条有向边, 然后对形成的图进行拓扑排序,

每次拿出的点放入大根堆中,依序输出并删边,最后将所有输出结果反序。

(第一次做理解成了“字典序最小”,就拿了二十分……)

原理上:倒着建图,用堆求出一个字典序最大的拓扑序,反过来输出即可。

正确性可以用反证法证明。

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

priority_queue<int> q;
vector<int> v[100010];

int c[100010], ans[100010];
int n, m;
int ecnt=0, acnt=0;

void dlt(int num){
    ans[acnt++] = num;
    for(int i=0; i<v[num].size(); i++){
        if(c[v[num][i]] == 1) q.push(v[num][i]);
        c[v[num][i]]--;    
    }
}
int main(){
    freopen("dictionary.in", "r", stdin);
    freopen("dictionary.out", "w", stdout);
    scanf("%d%d", &n, &m);

    for(int i=0; i<m; i++){
        int t1, t2;
        scanf("%d%d", &t1, &t2);
        v[t2].push_back(t1);
        c[t1]++;
    }
    for(int i=1; i<=n; i++){
        if(c[i] == 0){
            q.push(i);
        }
    }    
    while(true){
        if(q.empty()) break;
        int temp = q.top();
        q.pop();
        dlt(temp);
    }
    if(acnt==n){
        for(int i=n-1; i>=0; i--)
            printf("%d ", ans[i]);
    }
    else printf("-1");
    return 0;
}
原文地址:https://www.cnblogs.com/miserweyte/p/11270675.html