☆问题描述:
给定一张N个点M条边的有向图,判断该有向图是否存在环?
☆数据输入:
输入有若干组,第一行有两个整数N,M(N≤100000,M≤200000),表示N个点M条有向边。
接下来M行,每行两个整数U,V表示一条U到V的有向边。顶点编号1-N。
☆结果输出:
输出YES表示存在环,NO表示不存在环。
☆输入示例:
3 3
1 2
2 3
3 1
☆输出示例:
YES
解题思路:
拓扑排序裸题,没什么好说的。vector以及queue的使用能够让解决问题的过程更加顺畅
拓扑排序:对一个有向图里的所有点进行排序,该排序满足这样的条件:对有向图中任意两点u、v,若存在一条有向边从u指向v,那么在排序过程中u就要出现在v前面。当且仅当一个有向图为有向无环图(directed acyclic graph,或称DAG)时,才能得到对应于该图的拓扑排序,每一个有向无环图都至少存在一种拓扑排序。
过程: 先统计所有点的入度,首先找到的入度为0的点就排在第一位,然后把这个点在图里“删除”,并且这个点指向的所有点的入度都减一;重复以上操作直到图里所有点都被“删除”时,拓扑排序完成,若删除到最后仍有点留下,则该图存在环,无拓扑排序。
时间复杂度O(n+e),n为点的总数,e为边的总数。
//Matsuri
#include<bits/stdc++.h>
#define MAX 200009
using namespace std;
int n,m;
int in[MAX]; // 点的入度
queue<int>q;
vector<int>edge[MAX]; // 存图用,二维vector
// edge[i].size():与编号为i的点相连的其他点的数量; edge[i][]:访问和点i相连的其他点
vector<int>ans; // 拓扑排序序列
int main()
{
scanf("%d%d",&n,&m);
int u,v;
for (int i=1;i<=m;i++)
{
scanf("%d%d",&u,&v);
edge[u].push_back(v); // 建立图的过程,举例:u点连向v点,则edge[u][0]=v,若u点还有和别的点连则二维参数++
in[v]++;
}
for (int i=1;i<=n;i++) if (in[i]==0) q.push(i); // 遍历找到入度为0的点,然后入队列
while(!q.empty())
{
// 拓扑排序过程
int p=q.front(); //选择一个入度为0的点,然后出队列
q.pop();
ans.push_back(p);
for (int i=0;i<edge[p].size();i++)
{
int next=edge[p][i];
in[next]--;
if (in[next]==0) q.push(next);
}
}
if (ans.size()==n) puts("NO");
else puts("YES");
return 0;
}