请告诉我为什么这段代码无法分析是否存在循环 无方向图?代码如下。这是关于spoj的PT07Y问题。我正在做的是获取节点并执行dfs。在我访问同一个节点时执行dfs,然后我说有一个循环。在从节点执行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;
}
答案 0 :(得分:0)
该图被指定为无向图,但输入格式将只指定每条边一次(对于方向之一)。因此,当您从输入中读取类似 1 2
的行时,您必须将 1
的邻接表扩展为 2
,并将 2
的邻接表扩展为 {{ 1}}。但是,在您的程序中,您只存储第一个方向。因此,您对图表的表示将是不完整的。
要解决此问题,请扩展您的输入处理以存储两个方向:
1
您的 for (i = 0; i < m; i++) {
cin >> a >> b;
v[a].push_back(b);
v[b].push_back(a);
}
方法行为不正确:
如果找到目标顶点,dfs
方法将返回 dfs
。但是,此返回码不会沿递归传播。因此,当您在 1
方法中进行递归调用 dfs(v[n][i], v, a);
并且该调用返回 dfs
时,当前的 1
调用会忽略此结果。如果当前的 dfs
调用在当前邻接列表中没有找到目标顶点,那么它将完成 dfs
循环并返回 for
。
要解决此问题,您必须确保如果递归 0
调用返回 dfs
,则 1
循环中止并且 for
也由当前的 1
调用,例如:
dfs
应用此修复后,另一个问题被揭示:当 if (dfs(v[n][i], v, a, n) == 1) {
return 1;
}
方法在相邻顶点列表中发现目标顶点时,它不排除到达当前顶点的边。因此,如果您有一个图 dfs
并且您从 1 <-> 2
处开始 dfs
,目标顶点为 1
,您将检查包含 1
的相邻顶点列表。遍历从 2
到 1
的边,您将检查包含 2
的相邻顶点列表。然后,您的方法将报告已找到目标顶点,但这是无效的,因为它仅沿着您来的地方的边缘找到。
要解决此问题,您可以跟踪每个 1
调用的父顶点。最初,没有设置父级(即 dfs
)。当您从 -1
转到 a
时,您会将 b
作为父项传递。在 a
处,您将忽略 b
的邻接列表中的父 a
:
所以你的 b
看起来像像这样:
dfs
您的实现的性能不是最佳的(这会导致 SPOJ 超时)。您可以通过在 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;
}
函数中每次循环检查后不清除 vis
数组来改进这一点。相反,重用 main
数组并仅对尚未访问过的顶点执行循环检查:
vis
这足以让 SPOJ 接受您的代码。
但是,您可以进一步优化代码中的连通性检查:而不是在循环检查后清除 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));
数组并为每个顶点执行另一系列 vis
直到发现第二个组件,您可以重用 dfs
数组,并在循环检查后简单地检查是否有任何未访问的顶点。这将允许您完全省略第二个 vis
系列。