[英]Cycle in an undirected graph using dfs
請告訴我為什么這段代碼無法分析無向圖中是否存在循環? 代碼如下。 這是 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;
}
該圖被指定為無向的,但輸入格式將只指定每條邊一次(對於其中一個方向)。 因此,當您從輸入中讀取像1 2
這樣的行時,您必須將1
的鄰接列表擴展2
,並將2
的鄰接列表擴展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
循環中止並且當前dfs
調用也返回1
,例如:
if (dfs(v[n][i], v, a, n) == 1) {
return 1;
}
應用此修復程序后,會出現另一個問題:當dfs
方法在相鄰頂點列表中發現目標頂點時,它不排除到達當前頂點的邊。 因此,如果您有一個圖1 <-> 2
並且您從1
開始dfs
,目標頂點為1
,您將檢查包含2
的相鄰頂點列表。 從1
到2
遍歷邊,您將檢查包含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
直到發現第二個組件,您可以重用vis
數組並簡單地檢查是否存在是循環檢查后任何未訪問的頂點。 這將允許您完全省略第二個dfs
系列。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.