[英]Cycle in an undirected graph using dfs
Please tell me why this code fails to analyse if there is a cycle in the un-directed graph?请告诉我为什么这段代码无法分析无向图中是否存在循环? The code is below.
代码如下。 This is the PT07Y question on spoj.
这是 spoj 上的 PT07Y 问题。 What i am doing is taking a node and doing the dfs.
我正在做的是获取一个节点并执行 dfs。 While performing dfs if I visit the same node then I say there is a cycle.
在执行 dfs 时,如果我访问同一个节点,那么我说有一个循环。 After performing dfs from a node, I make the visited array false and perform for next node until I get a cycle or all nodes are visited.
从一个节点执行 dfs 后,我将访问的数组设置为 false 并为下一个节点执行,直到我得到一个循环或访问所有节点。
#include <iostream>
#include <bits/stdc++.h>
using namespace std;
bool vis[10001];
int dfs(int n,vector <int> v[],int a)
{
vis[n]=true;
for(int i=0;i<v[n].size();i++)
{
if(v[n][i]==a)
return 1;
if(vis[v[n][i]]==false)
{
dfs(v[n][i],v,a);
}
}
return 0;
}
int main()
{
ios_base::sync_with_stdio(false);
cin.tie(NULL);
int n,m,i,a,b,cc;
cin>>n>>m;
vector <int> v[n+1];
memset(vis,false,n+1);
for(i=0;i<m;i++)
{
cin>>a>>b;
v[a].push_back(b);
}
for(i=1;i<=n;i++)
{
if(dfs(i,v,i))
{
cout<<"NO"<<endl;
return 0;
}
memset(vis,false,sizeof(vis));
}
cc=-1;
for(i=1;i<=n;i++)
{
if(vis[i]==false)
{
cc++;
if(cc>0)
{
cout<<"NO"<<endl;
return 0;
}
int m= dfs(i,v,-1);
}
}
cout<<"YES"<<endl;
return 0;
}
The graph is specified to be undirected, but the input format will only specify each edge once (for one of the directions).该图被指定为无向的,但输入格式将只指定每条边一次(对于其中一个方向)。 So when you read from the input a line like
1 2
, you would have to extend the adjacency list of 1
by 2
and extend the adjacency list of 2
by 1
.因此,当您从输入中读取像
1 2
这样的行时,您必须将1
的邻接列表扩展2
,并将2
的邻接列表扩展1
。 However, in your program you only store the first direction.但是,在您的程序中,您只存储第一个方向。 Therefore your representation of the graph will be incomplete.
因此,您对图表的表示将是不完整的。
To fix this, extend your input processing to store both directions:要解决此问题,请扩展您的输入处理以存储两个方向:
for (i = 0; i < m; i++) {
cin >> a >> b;
v[a].push_back(b);
v[b].push_back(a);
}
Your dfs
method behaves incorrectly:您的
dfs
方法行为不正确:
The dfs
method returns 1
if it finds the target vertex. dfs
方法在找到目标顶点时返回1
。 However, this return code is not propagated along the recursion.但是,此返回代码不会沿递归传播。 So when you make the recursive call
dfs(v[n][i], v, a);
所以当你进行递归调用时
dfs(v[n][i], v, a);
inside your dfs
method, and that call returns 1
, this result is ignored by the current dfs
call.在您的
dfs
方法中,并且该调用返回1
,当前dfs
调用将忽略此结果。 If the current dfs
call does not find the target vertex in the current adjacency list, it will then complete the for
loop and return 0
.如果当前的
dfs
调用没有在当前的邻接表中找到目标顶点,那么它将完成for
循环并返回0
。
To fix this, you would have to make sure that if the recursive dfs
call returns 1
, the for
loop is aborted and the 1
is also returned by the current dfs
call, eg:要解决此问题,您必须确保如果递归
dfs
调用返回1
,则for
循环中止并且当前dfs
调用也返回1
,例如:
if (dfs(v[n][i], v, a, n) == 1) {
return 1;
}
With this fix applied, another problem is revealed: When the dfs
method discovers the target vertex in the list of adjacent vertices, it does not rule out the edge from where the current vertex was reached.应用此修复程序后,会出现另一个问题:当
dfs
方法在相邻顶点列表中发现目标顶点时,它不排除到达当前顶点的边。 So if you have a graph 1 <-> 2
and you start dfs
at 1
with target vertex 1
, you would examine the list of adjacent vertices, containing 2
.因此,如果您有一个图
1 <-> 2
并且您从1
开始dfs
,目标顶点为1
,您将检查包含2
的相邻顶点列表。 Traversing the edge from 1
to 2
, you would examine the list of adjacent vertices, containing 1
.从
1
到2
遍历边,您将检查包含1
的相邻顶点列表。 Your method would then report that the target vertex has been found, but this is not valid because it has only been found along the edge from where you came.然后,您的方法将报告已找到目标顶点,但这是无效的,因为它只在您来的地方的边上找到。
To fix this, you could track the parent vertex for each dfs
call.要解决此问题,您可以跟踪每个
dfs
调用的父顶点。 Initially, no parent is set (ie -1
).最初,没有设置父级(即
-1
)。 When you go from a
to b
, you would pass a
as the parent.当您从
a
转到b
时,您会将a
作为父级传递。 At b
, you would then ignore the parent a
in the adjacency list of b
:在
b
处,您将忽略b
a
So your dfs
would look eg like this:所以你的
dfs
看起来像这样:
int dfs(int n, vector<int> v[], int a, int parent) {
vis[n] = true;
for (int i = 0; i < v[n].size(); i++) {
if (v[n][i] == parent) {
continue;
}
if (v[n][i] == a)
return 1;
if (vis[v[n][i]] == false) {
if (dfs(v[n][i], v, a, n) == 1) {
return 1;
}
}
}
return 0;
}
The performance of your implementation is not optimal (which causes a time-out on SPOJ).您的实施性能不是最佳的(这会导致 SPOJ 超时)。 You can improve this by not clearing the
vis
array after each cycle check in your main
function.您可以通过在每次循环检查
main
函数后不清除vis
数组来改进这一点。 Instead, reuse the vis
array and only perform the cycle check on vertices that have not been visited yet:相反,重用
vis
数组并只对尚未访问过的顶点执行循环检查:
for (i = 1; i <= n; i++) {
if (vis[i] == false) {
if (dfs(i, v, i, -1)) {
cout << "NO" << endl;
return 0;
}
}
}
memset(vis, false, sizeof(vis));
This is enough to get your code accepted by SPOJ.这足以让 SPOJ 接受您的代码。
However, you could further optimize the connectivity check in your code: Instead of clearing the vis
array after the cycle check and performing another series of dfs
for each vertex until a second component is discovered, you could reuse the vis
array and simply check if there are any unvisited vertices after the cycle checks.但是,您可以进一步优化代码中的连通性检查:不是在循环检查后清除
vis
数组并为每个顶点执行另一系列dfs
直到发现第二个组件,您可以重用vis
数组并简单地检查是否存在是循环检查后任何未访问的顶点。 This would allow you to omit the second dfs
series completely.这将允许您完全省略第二个
dfs
系列。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.