题意:
有一个比赛,每次由两个参赛人员进行比赛,现知道每场比赛的结果,求字典序最小的名次序列。
输入要求:
输入有若干组,每组中的第一行为二个数N(1<=N<=500),M;其中N表示猫猫的个数,M表示接着有M行的输入数据。接下来的M行数据中,每行也有两个整数P1,P2表示即编号为 P1 的猫猫赢了编号为 P2 的猫猫。
输出要求:
给出一个符合要求的排名。输出时猫猫的编号之间有空格,最后一名后面没有空格!
输入样例:
4 3
1 2
2 3
4 3
输出要求:
1 2 4 3
思路:
这是典型的拓扑排序问题,每次比赛插入一条有向边,然后按字典序最小输出拓扑序列。
- 在插入边时,每次给相应的目的点的入度加一。定义一个小根堆q,遍历所有点,把入度为0的点插入小根堆中。
- 构造一个记录答案的数组ans,然后对小根堆q进行操作,每次从堆中取出一个元素 now 记入数组ans,将顶点 now 指向的所有点的入度减一,若减一之后指向的点入度归零,则将该点也入堆。
- 小根堆清空后,顺序输出 ans 数组中的元素即可。
代码:
#include <xfunctional>
#include <iostream>
#include <vector>
#include <queue>
using namespace std;
int N, M;
//vector<int> graph[505];
//int in_deg[505];
int main()
{
while (scanf_s("%d%d", &N, &M) != EOF) {//N个点,M条边
vector<int> graph[505];
int a, b, in_deg[505];
memset(in_deg, 0, sizeof(in_deg));
//输入
for (int i = 0; i < M; i++) {
cin >> a >> b;
graph[a].push_back(b);
in_deg[b]++;
}
priority_queue<int, vector<int>, greater<int> > q;
//先将入度为 0 的点放进小根堆中
for (int i = 1; i <= N; i++) {
if (in_deg[i] == 0) {
q.push(i);
}
}
//记录答案的数组
vector<int> ans;
while (q.size()) {
//从小跟堆拿出一个,记在答案里面
int now = q.top(); q.pop(); ans.push_back(now);
//所有边指向的点入度减一,删掉这条边,如果指向的点入度成 0 了,把它也入堆
int size = graph[now].size();
for (int i = 0; i < size; i++) {
in_deg[graph[now][i]]--;
if (in_deg[graph[now][i]] == 0) {
q.push(graph[now][i]);
}
}
graph[now].clear();
}
cout << ans[0];
for (int i = 1; i < N; i++) {
cout << " " << ans[i];
}
cout << endl;
}
}