请告诉我为什么这段代码不能分析无向图中是否有循环?代码如下。这是Spoj上的PT07Y问题。我正在做的是取一个节点并执行dfs。当执行dfs时,如果我访问同一个节点,那么我说有一个循环。从一个节点执行dfs后,我使访问的数组为假,并为下一个节点执行,直到我得到一个周期或所有节点都被访问。
#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;
}
图形被指定为无向,但输入格式将只指定每条边一次(对于其中一个方向)。因此,当您从输入中读取类似于12
的行时,您必须通过2
扩展1
的邻接列表,并通过1
扩展2
的邻接列表。但是,在您的程序中,您只存储第一个方向。因此,图形的表示将不完整。
要解决此问题,请扩展输入处理以存储两个方向:
for (i = 0; i < m; i++) {
cin >> a >> b;
v[a].push_back(b);
v[b].push_back(a);
}
您的dfs
方法行为不正确:
dfs
方法如果找到目标顶点,则返回1
。但是,这个返回代码不会沿着递归传播。因此,当您在dfs
方法中进行递归调用dfs(v[n][i], v, a);
,并且该调用返回1
时,当前的dfs
调用将忽略此结果。如果当前dfs
调用没有在当前邻接列表中找到目标顶点,那么它将完成循环的并返回
0
。
要解决此问题,您必须确保如果递归
dfs
调用返回1
,则for
循环被中止,当前dfs
调用也会返回1
,例如:
if (dfs(v[n][i], v, a, n) == 1) {
return 1;
}
应用此修复程序后,会发现另一个问题:当
dfs
方法在相邻顶点列表中发现目标顶点时,它不排除当前顶点所在的边缘。所以如果你有一个图1
要解决此问题,可以跟踪每个
dfs
调用的父顶点。最初,没有设置父项(即-1
)。当您从a
转到b
时,您将以父级身份传递a
。在b
,您将忽略b
的邻接列表中的父a
:
所以你的
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;
}
实现的性能不是最佳的(这会导致SPOJ超时)。您可以通过在
main
函数中的每次循环检查后不清除vis
数组来改善这一点。相反,重新使用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));
这足以让您的代码被SPOJ接受。
然而,您可以进一步优化代码中的连接性检查:您可以重用
数组,并在循环检查后简单地检查是否有任何未访问的顶点。这将允许您完全省略第二个,而不是在循环检查后清除vis
数组,并为每个顶点执行另一系列dfs
,直到发现第二个组件VISdfs
系列。
因此,我为DFS编写了以下代码: 现在,我读到,在一个无向图中,如果当DFS时,它再次返回到同一个顶点,有一个循环。所以我做的是这样,, 但是,我的检查周期函数返回true,即使他们没有周期。那是为什么呢?我的功能有问题吗?没有执行问题,否则我早就调试了,但他们似乎在我的逻辑中有问题。
假设我有一个有V节点和E边的无向图。如果我用邻接列表表示图形,如果我有x和y之间边的表示,我还必须在邻接列表中有y和x之间边的表示。 我知道有向图的DFS具有VE复杂性。对于无向图,它没有v 2*e复杂性,因为您访问每个边2次?抱歉,如果这是一个愚蠢的问题...我真的很想明白这个想法。谢谢你,
我很难理解Kosaraju寻找有向图的强连通成分的算法。以下是我笔记本上的内容(我是学生:D): 从任意顶点开始(用#1标记)并执行DFS。如果无法继续,请使用#2标记上次访问的顶点,然后启动另一个DFS(跳过已标记的顶点),依此类推。 变换图形的位置。 按相反顺序执行DFS从每个顶点开始,在每个DFS之后结束访问的顶点属于同一个SCC 我举了一个例子: 从E开始的第一步后,标签为: E G K
你有一辆2005年本田雅阁,油箱里还剩50英里(最大重量)。在50英里半径范围内,你可以访问哪些麦当劳的位置(图节点)?这是我的问题。 如果你有一个加权有向无环图,你如何找到在给定的权限制内可以访问的所有节点? 我知道Dijkstra的算法,但我似乎找不到任何关于它在最小路径问题之外的用途的文档。在我的例子中,我们没有特别想结束的节点,我们只想在不超过最大权重的情况下尽可能多地结束。似乎您应该能够
本文会围绕算法中DFS求有向图或无向图两点间所有路径,先讲解DFS以及有向图或无向图的意思。 有向图在图中的边是有方向的,表现出来就是有个箭头指示方向,节点只能单向通信或传递消息,相当于单行道,无向图边没方向是双向的,边连接的两个节点有通路可以双向通信,类似于双行道。 无向图,边没有方向的图称为无向图。邻接矩阵则是对称的,且只有0和1,因为没有方向的区别后,要么有边,要么没边。 DFS作为搜索算法
我不太确定这是不是解决办法,有人能帮我弄清楚吗? 谢谢!