题目分析:
拓扑排序经典例题,图中的顶点之间具有明显地先后关系,{u,v} 代表u队战胜了v队,而题目要求恰好是输出其排名,很明显,u肯定比v排名靠前,因此先输出u
需要注意的是,这个题要求编号小的尽量排在前面,为此,我们不能像别的题目那样,一次性将当前图中所有入度为0的顶点入队,而是从编号为1的顶点开始枚举,找到一个没有用过的顶点并且这个顶点的入度为0,那么将这个点入队列,并退出枚举。
而且我们每次操作完一个定点后需要找下一个顶点,这样说起来,这个题完全不需要队列,因为每次操作的顶点都在上一次操作完后继续找入度为0的顶点,而且只找一个。
代码区
#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cstring>
#include<string>
#include<queue>
#include<stack>
#include<vector>
#include<map>
#include<set>
using namespace std;
typedef long long ll;
const int inf = 0x3f3f3f3f;
const int Max = 3e5 + 10;
int n, m;
vector<int>v[Max]; //邻接表
queue<int>q; //保存入度为0的顶点
queue<int>a; //记录拓扑排序
int degree[Max]; //记录各个点的入度
int num[1000];
void toposort()
{
for (int i = 1; i <= n; i++)
{
if (!degree[i])
{
q.push(i), degree[i] = -1;
//degree[i] == -1 代表这个顶点已经访问过了,主要是用于确定多个顶点度数为0的时候
//用枚举先将编号小的入队列,同时为了防止之前的顶点也因为degree[x] == 0而入队列
//特此加一个特殊值表示已经用过了的顶点
break; //这个break在这个题中是有必要的,因为需要编号最小的点先输出的原因
}
}
while (!q.empty())
{
int now = q.front();
q.pop();
a.push(now);
for (int i = 0;i < v[now].size();i++)
{
int to = v[now][i];
degree[to]--;
}
for (int i = 1; i <= n; i++) //按编号大小顺序将顶点入队
{
if (!degree[i]) //已经用过了的顶点不可以再用
{
q.push(i), degree[i] = -1;
break; //这个break在这个题中是有必要的,因为需要编号最小的点先输出的原因
}
}
}
}
int main()
{
std::ios::sync_with_stdio(false);
while (cin >> n >> m)
{
memset(degree, 0, sizeof(degree));
while (!q.empty())q.pop();
for (int i = 1;i <= n; i++)
{
v[i].clear();
}
while (m--)
{
int s, e;
cin >> s >> e;
v[s].push_back(e);
degree[e]++;
}
toposort();
cout << a.front();
a.pop();
while (!a.empty())
{
cout << " " << a.front();
a.pop();
}
cout << endl;
}
return 0;
}