原题: http://codeforces.com/problemset/problem/915/D
题意: 一个n<500,m<1e5的有向图,你可以删除其中一条边,问是否可能不存在环。
解析:
自己想就是怎么找到一条边使所有的环都经过这条边。答案一定在两个环的公共边中,然后各种问题各种解决……
到头来还是用了别人的方法,写这篇博客的时候心情压抑啊。(不得不承认方法是真的好)
先是方法: 因为边太多,不可能枚举每条边做一遍。但是点只有500,可以枚举每一个有入度的点,让这个点入度-1再跑。
这里还是自己证明一下正确性吧,安心一点……
证明:
就结果而言,每条边造成的结果只是让一个点的入度+1而已。
假设没有进行-1操作,有一个点,在拓扑排序后入度只剩1,说明我们可以删除其后面的那条边使之入度为0,那么这个点就会进入队列将所在环删除。
而枚举每个点使之入度-1,就相当于上面的将最后一条边删除。所以似乎可以通过记录删除的边号来得出:需要删除的哪条边。
#include<bits/stdc++.h>
using namespace std;
const int maxn=509,maxm=1e5+9;
int x[maxm],y[maxm];
int n,m;
vector<int>Ed[maxn];
bool vis[maxm];
int in[maxn];
int tmpin[maxn];
int cnt;
void topology(){
queue<int>Q;
cnt=0;
for(int i=1;i<=n;i++){
if(!tmpin[i])Q.push(i),cnt++;
}
while(!Q.empty()){
int p=Q.front();Q.pop();
for(int i=0;i<Ed[p].size();i++){
int ed=Ed[p][i];
vis[ed]=1;
tmpin[y[ed]]--;
if(tmpin[y[ed]]==0)Q.push(y[ed]),cnt++;
}
}
}
int main(){
scanf("%d%d",&n,&m);
for(int i=1;i<=m;i++){
scanf("%d%d",x+i,y+i);
in[y[i]]++;
Ed[x[i]].push_back(i);
}
for(int i=1;i<=n;i++){
if(in[i]){
memcpy(tmpin,in,sizeof(in));
tmpin[i]--;
topology();
if(cnt==n){
return 0*printf("YES\n");
}
}
}
printf("NO\n");
}