題面
題意:
給n個點,m條邊,有向圖,求是否能去掉一條邊使得原圖無環。
\(n<= 500, m <= min(n(n - 1), 10^5)\)
題解
一個樸素的想法:
枚舉刪哪條邊,然後用拓撲排序判斷是否還有環。
但是複雜度直接爆炸。
我們考慮刪邊對我們check過程(拓撲排序)的影響。
刪掉一條邊,相當於使得它的出點入度減一。
那麼不管我們刪掉哪條邊,只要它的出點一致,那麼在拓撲排序看來,我們的操作其實效果一樣。
也就是這樣會造成大量無用且重複的判斷。
所以我們考慮枚舉點。
如果一個點有入度,那麼我們嘗試刪除一條到這個點的邊,也就是讓這個點入度減一,然後用拓撲排序判斷一下是否有環。
#include<bits/stdc++.h>
using namespace std;
#define R register int
#define AC 600
#define ac 101000
int n, m;
int in[AC], s[AC];
int q[AC], head, tail;
int Head[AC], date[ac], Next[ac], tot;
inline int read()
{
int x = 0;char c = getchar();
while(c > '9' || c < '0') c = getchar();
while(c >= '0' && c <= '9') x = x * 10 + c - '0', c = getchar();
return x;
}
inline void add(int f, int w){//加單項邊
date[++ tot] = w, Next[tot] = Head[f], Head[f] = tot, in[w] ++, s[w] ++;
}
void pre()
{
n = read(), m = read();
for(R i = 1; i <= m; i ++)
{
int x = read(), y = read();
add(x, y);
}
}
bool t_sort()
{
head = 1, tail = 0;
for(R i = 1; i <= n; i ++)
if(!in[i]) q[++ tail] = i;
while(head <= tail)
{
int x = q[head ++];
for(R i = Head[x]; i; i = Next[i])
{
int now = date[i];
if(!(-- in[now])) q[++ tail] = now;
}
}
return tail == n;
}
void work()
{
for(R i = 1; i <= n; i ++)
{
if(!in[i]) continue;
-- in[i];
if(t_sort())
{
printf("YES\n");
return ;
}
memcpy(in, s, (n + 1) * 4);
// for(R j = 1; j <= n; j ++) in[j] = s[j];
}
printf("NO\n");
}
int main()
{
// freopen("in.in", "r", stdin);
pre();
work();
return 0;
}