Week8:猫猫向前冲——拓扑排序

题目大意
众所周知, TT 是一位重度爱猫人士,他有一只神奇的魔法猫。
有一天,TT 在 B 站上观看猫猫的比赛。一共有 N 只猫猫,编号依次为1,2,3,…,N进行比赛。比赛结束后,Up 主会为所有的猫猫从前到后依次排名并发放爱吃的小鱼干。不幸的是,此时 TT 的电子设备遭到了宇宙射线的降智打击,一下子都连不上网了,自然也看不到最后的颁奖典礼。
不幸中的万幸,TT 的魔法猫将每场比赛的结果都记录了下来,现在他想编程序确定字典序最小的名次序列,请你帮帮他。

输入格式
输入有若干组,每组中的第一行为二个数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

解题思路
猫猫们之间的胜负关系可以构成一张有向无环图。
P1 赢了 P2 等价于结点 P1 到结点 P2 有一条边且意味着在最终的名次序列中 P1 要在 P2 的前面。
因此这道题使用拓扑排序的思路,使用Kahn算法。

Kahn算法:

  • 将入度为 0 的点组成一个集合 S。
  • 每次从 S 里面取出一个顶点 u (可以随便取)放入 L , 然后遍历顶点 u 的所有边 (u, v), 并删除之,并判断如果该边的另一个顶点 v ,如果在移除这一条边后入度为 0 , 那么就将这个顶点放入集合 S 中。不断地重复取出顶点然后重复这个过程。
  • 最后当集合为空后,就检查图中是否存在任何边。如果有,那么这个图一定有环路,否者返回 L L 中顺序就是拓扑排序的结果。

本题要求的是字典序最小的答案,所以这里我们使用优先队列来模拟上述的集合S:

#include <iostream>
#include <vector>
#include <queue>
#include <cstring>

using namespace std;

vector<int>cat[501];
int in_deg[501];

int main()
{
	int n, m;
	priority_queue<int, vector<int>, greater<int>>q;
	vector<int>ans;

	while (cin >> n >> m)
	{
		memset(in_deg, 0, sizeof in_deg);
		while (!q.empty()) q.pop();
		for (int i = 0; i < 501; i++) cat[i].clear();
		ans.clear();

		for (int i = 0, a, b; i < m; i++)
		{
			cin >> a >> b;
			cat[a].push_back(b);
			in_deg[b]++;
		}

		for (int i = 1; i <= n; i++)
		{
			if (!in_deg[i]) q.push(i);
		}
		while (!q.empty())
		{
			int out = q.top();
			q.pop();
			ans.push_back(out);
			
			for (int i = 0; i < cat[out].size(); i++)
			{
				int v = cat[out][i];
				if (--in_deg[v] == 0)
				{
					q.push(v);
				}
			}
		}

		for (int i = 0; i < ans.size(); i++)
		{
			if (i != ans.size() - 1)
				cout << ans[i] << ' ';
			else
				cout << ans[i] << endl;
		}

	}
}
原创文章 52 获赞 7 访问量 1680

猜你喜欢

转载自blog.csdn.net/qq_44506233/article/details/105578420