暑期集训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; }