Точки артикуляции, появляющиеся неоднократно в реализации Тарьяна

Недавно я выучил алгоритм линейного времени для вычисления точек сочленения в графе. Моя реализация правильно работает с данными онлайн-теста судьи, поэтому с кодом нет проблем. Тем не менее, мне кажется, что мне трудно понять, как одна и та же точка сочленения встречается чаще, чем одна в прогоне DFS. Позволь мне объяснить

У меня есть список, в котором хранятся точки сочленения, если они встречаются. Теперь, когда я печатаю список в конце, я получаю правильные точки сочленения, но несколько вершин, которые являются точками сочленения, встречаются более одного раза в списке. По моему мнению, этого не должно происходить, поскольку мы встречаем каждую вершину только ОДИН РАЗ. Так почему же я получаю повторяющиеся записи в списке? Чтобы решить эту проблему, я использовал HashSet в своем исходном коде для их хранения и просто напечатал содержимое в конце, которое дало правильный ответ. Вот мой код с проблемой. Алгоритм в основном основан на псевдокоде из Википедии здесь: https://en.wikipedia.org/wiki/Biconnected_component

Вот мой код для внедрения в C ++:

/*input
7 6
0 1
1 2
3 4
2 4
2 6
5 2
*/
#include <bits/stdc++.h>
using namespace std;
#define endl '\n'
#define pb emplace_back
#define sz 3005 //In the current scenario, I need only a maximum on 3000 vertices

typedef long long int ll;

//Created by Shreyans Sheth [bholagabbar]

bool visited [sz]; //whether the node has been discoverd in the DFS run or not
int low [sz]; //time of the earliest discovered vertex reachable from the vertex
int disc [sz]; //time at which vertex was explored
int parent [sz]; //stores the parents of each vertex
vector<int> a[sz]; //Adjacency List for graph
int rtime; //Time
vector<int> ap; //Stored the articulation points

void DFS(int s)
{
visited[s]=1;
low[s]=disc[s]=++rtime;
int nchild=0;
for(auto i:a[s])
{
if(!visited[i])
{
nchild++;//INcrement children of the current vertex
parent[i]=s;
DFS(i);
low[s]=min(low[s],low[i]);
/* s is an articulation point iff
1. It the the root and has more than 1 child.
2. It is not the root and no vertex in the subtree rooted at one of its
children has a back-link to its ancestor.
A child has a back-link to an ancestor of its parent when its low
value is less than the discovery time of its parent.*/
if((parent[s]==-1 && nchild>1)||(parent[s]!=-1 && low[i]>=disc[s]))
ap.pb(s);//Adding the articulation points. How are they repeated?
}
else if(visited[i] && i!=parent[s])
low[s]=min(low[s],disc[i]);
}

}

void ArticulationPoints(int n)//Driver Funtion
{
ap.clear();
rtime=0;//The time for each cycle of DFS
for(int i=0;i<n;i++)
{
parent[i]=-1;//Initializing parents as -1. True for roots
visited[i]=0;//All points not visited
low[i]=disc[i]=INT_MAX;
}
for(int i=0;i<n;i++)
if(!visited[i])//Vertex not discoverdd
DFS(i);
}

int main()
{
int n,m;//number of vertices, edges
cin>>n>>m;
for(int i=0;i<m;i++)//Building Graph
{
int x,y;
cin>>x>>y;
a[x].pb(y);
a[y].pb(x);
}
ArticulationPoints(n);//Calculating Articulation points
cout<<"Articulation Points are:\n";
for(int i:ap)
cout<<i<<endl;
return 0;
}

Код с вводом и выводом: http://ideone.com/u5dYOy (Видите, как 2 приходит трижды?)

Почему это происходит? Я что-то упустил в алгоритме. Я считаю, что у меня есть четкое представление о работе алгоритма. Любая помощь приветствуется. Спасибо

1

Решение

#include <bits/stdc++.h>

Не делай этого.

Кроме этого, ваш код отличается от псевдокода несколькими способами. Для справки вот псевдокод, с которым вы связаны:

GetArticulationPoints(i, d)
visited[i] = true
depth[i] = d
low[i] = d
childCount = 0
isArticulation = false
for each ni in adj[i]
if not visited[ni]
parent[ni] = i
GetArticulationPoints(ni, d + 1)
childCount = childCount + 1
if low[ni] >= depth[i]
isArticulation = true
low[i] = Min(low[i], low[ni])
else if ni <> parent[i]
low[i] = Min(low[i], depth[ni])
if (parent[i] <> null and isArticulation) or (parent[i] == null and childCount > 1)
Output i as articulation point
  1. У вас нет d параметр. Вместо этого вы увеличиваете глобальную переменную. Но вы никогда не уменьшаете эту переменную, поэтому она будет увеличиваться при посещении большего количества узлов. В псевдокоде d представляет текущую глубину, в которой вы находитесь в дереве. Два родных брата должны иметь одинаковую глубину, но в вашем случае один будет иметь большую глубину.

    Насколько я понимаю, это не имеет значения для этого алгоритма, но, тем не менее, он может быть источником ошибок в целом, если вы не следуете псевдокоду. И глобальных переменных следует избегать в любом случае.

    Решение: добавить int d параметр вашей функции и обрабатывать его так, как показывает псевдокод: добавив + 1 к нему всякий раз, когда вы вызываете функцию рекурсивно. Начальное значение может быть любым, но обычно оно установлено 0 или же 1,

  2. Ваш if условия и более сложные, чем в псевдокоде. Я не знаю, если они обязательно ошибаются, но это, в сочетании с разными именами, которые вы используете, может привести к ошибкам. Если вы реализуете впервые, и вы сильно полагаетесь на псевдокод, я предлагаю вам придерживаться его стиля.

    Решение: изменить DFS функция для:

    void DFS(int s, int d)
    {
    visited[s]=1;
    low[s]=disc[s]=d;
    int nchild=0;
    int isArticulation = 0;
    for(auto i:a[s])
    {
    if(!visited[i])
    {
    nchild++;//INcrement children of the current vertex
    parent[i]=s;
    DFS(i, d + 1);
    low[s]=min(low[s],low[i]);
    /* s is an articulation point iff
    1. It the the root and has more than 1 child.
    2. It is not the root and no vertex in the subtree rooted at one of its
    children has a back-link to its ancestor.
    A child has a back-link to an ancestor of its parent when its low
    value is less than the discovery time of its parent.*/
    if (low[i] >= disc[s])
    isArticulation = 1;
    }
    else if(i != parent[s])
    low[s] = min(low[s], disc[i]);
    }
    
    if ((parent[s] != -1 && isArticulation) || (parent[s] == -1 && nchild > 1))
    ap.pb(s);
    }
    

    У тебя был последний if внутри вашего if not visited условие, которое я предполагаю, что является причиной ваших дубликатов (потому что может быть несколько i такой, что low[i] >= disc[s]Таким образом, вы сохраняли точку сочленения для всех них), хотя я не проверял это.

Я также предлагаю вам использовать лучшие имена переменных, чтобы вы знали, что и что представляет. Это также облегчит понимание реальной логики алгоритма.

1

Другие решения


По вопросам рекламы [email protected]