题意翻译
给定一个n个顶点,m条边的有向图。你允许从其中去掉最多一条边。
你能够去掉最多一条边就让这个图无环吗?我们称一个有向图无环,当且仅当它不包含一个环(起点和终点相同的路径)。
输入格式:
第一行两个正整数n,mn,m 2\le n\le 500,1\le m\le min(n(n-1),100000)2≤n≤500,1≤m≤min(n(n?1),100000) ,代表图的顶点数和边数。
接下来mm 行,每行两个数u,vu,v ,表示有一条从uu 到vv 的有向边(1\le u,v\le n,u\ne v1≤u,v≤n,u≠v )。一对(u,v)(u,v) 最多出现一次。
输出格式:
如果有可能把这个图变成无环图,输出YES,否则输出NO。
说明:
第一个样例中,去掉2\to 32→3 的边即可。
第二个样例中,需要至少去掉两条边(例如2\to 12→1 和2\to 32→3 )才能让这个图变成无环图。
Translated by @小粉兔
题目描述
You are given a directed graph consisting of nn vertices and mm edges (each edge is directed, so it can be traversed in only one direction). You are allowed to remove at most one edge from it.
Can you make this graph acyclic by removing at most one edge from it? A directed graph is called acyclic iff it doesn‘t contain any cycle (a non-empty path that starts and ends in the same vertex).
输入输出格式
输入格式:
The first line contains two integers nn and mm ( 2<=n<=5002<=n<=500 , 1<=m<=min(n(n-1),100000)1<=m<=min(n(n?1),100000) ) — the number of vertices and the number of edges, respectively.
Then mm lines follow. Each line contains two integers uu and vv denoting a directed edge going from vertex uu to vertex vv( 1<=u,v<=n1<=u,v<=n , u≠vu≠v ). Each ordered pair (u,v)(u,v) is listed at most once (there is at most one directed edge from uu to vv ).
输出格式:
If it is possible to make this graph acyclic by removing at most one edge, print YES. Otherwise, print NO.
输入输出样例
说明
In the first example you can remove edge , and the graph becomes acyclic.
In the second example you have to remove at least two edges (for example, and ) in order to make the graph acyclic.
果然样例都是骗人的www,疯狂WA并不知道我的dfs大法哪里错了。。。。
先贴一下WA代码,,,突然有点思路再去换一种方式怼怼
#include<iostream> #include<cstdio> #include<cstdlib> #include<algorithm> #include<cstring> #define ll long long #define maxn 505 #define maxm 100005 using namespace std; int to[maxm],ne[maxm]; int hd[maxn],n,m,cnt[maxm]; int circle,st[maxn],tp; int a[maxn][maxn]; bool v[maxn]; bool dfs(int x){ v[x]=1; st[++tp]=x; bool can=1; for(int i=hd[x];i;i=ne[i]){ if(!v[to[i]]){ if(!dfs(to[i])){ can=0; break; } } else{ bool flag=0; circle++,cnt[i]++; if(cnt[i]==circle) flag=1; for(int j=tp;j>1;j--){ if(st[j]==to[i]) break; if((++cnt[a[st[j-1]][st[j]]])==circle) flag=1; } if(!flag){ can=0; break; } } } tp--; if(can) return 1; else return 0; } int main(){ scanf("%d%d",&n,&m); int uu,vv; for(int i=1;i<=m;i++){ scanf("%d%d",&uu,&vv),a[uu][vv]=i; to[i]=vv,ne[i]=hd[uu],hd[uu]=i; } for(int i=1;i<=n;i++) if(!v[i]) if(!dfs(i)){ puts("NO"); return 0; } puts("YES"); return 0; }
突然意识到了我这个算法的错误之处orz,虽然v[to[i]]为真的时候可能是找到了一个环,但是很可能此时栈中已经没有to[i]了(注意这是有向图啊,
可以先搜到to[i]然后发现扩展不出去了之后退回来),而且还有v[to[i]]为真的时候没有找到一个环呢,,,,
所以,,,,貌似不能dfs大法好了www
好了终于过了。。。。
首先讲一下为什么不能枚举边然后dfs。
为什么很多人觉得图的遍历的复杂度是O(N)?????
难道你不得把边都扫一遍吗???O(N+M)这是理论下界、、、
所以暴力枚举边+DFS是O(M^2+N*M)的,这就是某些人炸掉还不知道自己哪里错了的原因hhhh
记得学拓扑排序的时候是说过有环的图不能拓排的,但是暴力枚举边肯定还不行。
可以发现的是删一条边对拓扑排序的影响可以只有某个点的入度-1了,我们可以不用准确的知道这条边是哪条,
因为就算再图中不删这条边的话也顶多会让一个原本就能进队列的点最后的入度变成-1,这个是没有什么影响的(当然如果你写拓排入队列的判断
是id[x]<=0的话就当我没说。。。);但是还有一个我们想要的性质:如果这条边的终点原本进步了队列且终止入度为1的话,删这条边就恰好能让它
进来了。
这样做就可以了,复杂度O(N*N+N*M)
#include<iostream> #include<cstdio> #include<cstdlib> #include<algorithm> #include<cstring> #include<queue> #define ll long long #define maxn 505 #define maxm 100005 using namespace std; int to[maxm],ne[maxm]; int hd[maxn],n,m,tot; int id[maxn],a[maxn]; inline bool solve(int x){ memcpy(a,id,sizeof(id)); a[x]--,tot=0; queue<int> q; for(int i=1;i<=n;i++) if(!a[i]) q.push(i),tot++; while(!q.empty()){ x=q.front(),q.pop(); for(int i=hd[x];i;i=ne[i]) if(!(--a[to[i]])){ q.push(to[i]); tot++; } } return (tot==n); } int main(){ int uu,vv; scanf("%d%d",&n,&m); for(int i=1;i<=m;i++){ scanf("%d%d",&uu,&vv); id[vv]++,to[i]=vv,ne[i]=hd[uu],hd[uu]=i; } for(int i=1;i<=n;i++) if(solve(i)){ puts("YES"); return 0; } puts("NO"); return 0; }