【CodeForces】915 D. Almost Acyclic Graph 拓扑排序找环

【题目】D. Almost Acyclic Graph

【题意】给定n个点的有向图(无重边),问能否删除一条边使得全图无环。n<=500,m<=10^5。

【算法】拓扑排序

【题解】找到一个简单环,则欲删除的边一定经过该环。尝试环上的每一条边(至多n条边)后再次拓扑排序判断全图是否有环。

拓扑排序后定位到简单环:剩余图是环+环内DAG,DFS过程中将走入死路的点标-1,访问过标1,找到访问过的点就是简单环。换起始点直到找到环为止。

复杂度O(nm)。

#include<cstdio>
#include<queue>
#include<algorithm>
using namespace std;
const int maxn=600,maxm=100010;
struct edge{int v,from;}e[maxm];
int map[maxn][maxn],tot,cnt,n,m,first[maxn],p,vis[maxn],in[maxn],deg[maxn],suc[maxn];
queue<int>Q;
void insert(int u,int v){tot++;e[tot].v=v;e[tot].from=first[u];first[u]=tot;in[v]++;}
void dfs(int x,int fa){
    if(p||vis[x]==-1)return;
    if(vis[x]==1){p=x;suc[fa]=x;return;}
    vis[x]=1;
    for(int i=first[x];i;i=e[i].from)if(deg[e[i].v]>0){
        dfs(e[i].v,x);
        if(p){if(fa&&!suc[p])suc[fa]=x;break;}
    }
    if(!p)vis[x]=-1;
}
bool solve(int o){
    cnt=0;
    for(int i=1;i<=n;i++){deg[i]=in[i];if(i==e[o].v)deg[i]--;if(deg[i]==0)Q.push(i),cnt++;}
    while(!Q.empty()){
        int x=Q.front();Q.pop();
        for(int i=first[x];i;i=e[i].from)if(i!=o&&--deg[e[i].v]==0)Q.push(e[i].v),cnt++;
    }
    if(cnt==n)return 1;
    return 0;
}    
int main(){
    scanf("%d%d",&n,&m);
    for(int i=1;i<=m;i++){
        int u,v;
        scanf("%d%d",&u,&v);
        insert(u,v);map[u][v]=tot;
    }
    cnt=0;
    for(int i=1;i<=n;i++){deg[i]=in[i];if(in[i]==0)Q.push(i),cnt++;}
    while(!Q.empty()){
        int x=Q.front();Q.pop();
        for(int i=first[x];i;i=e[i].from)if(--deg[e[i].v]==0)Q.push(e[i].v),cnt++;
    }
    if(cnt==n){printf("YES");return 0;}
    for(int i=1;i<=n;i++)if(deg[i]>0&&!p)dfs(i,0);
    int pp=p;
    do{
        if(solve(map[p][suc[p]])){printf("YES");return 0;}
        p=suc[p];
    }while(p!=pp);
    printf("NO");
    return 0;
}
View Code

 

另一种解法:枚举点i,in[i]--,拓扑排序找环。这样相当于删除一条指向n的边后全图找环。

posted @ 2018-01-15 13:10  ONION_CYC  阅读(579)  评论(0编辑  收藏  举报