Tarjan演算法求割點 附裸題
阿新 • • 發佈:2019-02-10
無向圖中割點的概念:
割點:一個結點稱為割點(或者割頂)當且僅當去掉該節點及其相關的邊之後的子圖不連通。
判斷一個結點是否為割點有如下兩個條件:
條件一:若該結點u為根結點,它應有兩個及兩個以上的子結點。
條件二:若該結點u非根結點,它應有一個滿足條件的子結點v:該子結點v及其後代沒有反向邊可以連回結點u的祖先。
在Tarjan演算法中,有兩個points非常重要:
1.dfn[i]表示結點i被訪問的時間戳,即深度優先訪問次序。
2.low[i]表示結點i及其子樹的結點通過反向邊能到達的結點的最小dfn值。
下面貼一個求割點的模板
#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#define maxn 10005
using namespace std;
int n,m,x,y,root;
int tot; //邊的序號
int id; //深度優先數即遍歷次序
int Next[maxn],Head[maxn],Dst[maxn];
bool iscut[maxn];
int low[maxn],dfn[maxn];
void init()
{
tot=0;id=0;
memset(iscut,0,sizeof(iscut));
memset (low,0,sizeof(low));
memset(dfn,0,sizeof(dfn));
}
void add_edge(int x,int y)
{
tot++;
Next[tot]=Head[x];
Head[x]=tot;
Dst[tot]=y;
}
void dfs(int x,int pre)
{
int child=0;
low[x]=dfn[x]=++id;
for(int i=Head[x];i;i=Next[i])
{
int y=Dst[i];
if(!dfn[y])
{
dfs(y,x);
low[x]=min(low[x],low[y]);
if (x==root) child++;
if(x==root&&child>=2) //滿足條件一
iscut[root]=true;
if(x!=root&&low[y]>=dfn[x]) //滿足條件二
iscut[x]=true;
}
else if(y!=pre)//結點y在結點x之前已經訪問過了,且y不是x的父親結點,必有dfn[y]<dfn[x]
low[x]=min(low[x],dfn[y]);
}
}
int main()
{
init();
scanf("%d%d",&n,&m);
for(int i=0;i<m;i++)
{
scanf("%d%d",&x,&y);
add_edge(x,y);
add_edge(y,x);
}
for(int i=1;i<=n;i++)
if(!dfn[i])
{
root=i;
dfs(i,i);
}
printf("輸出割點:\n");
for(int i=1;i<=n;i++)
if(iscut[i])
printf("%d ",i);
printf("\n");
return 0;
}
其中low[x]=min(low[x],dfn[y])這個地方,起初讓我很不理解,為什麼不可以是low[x]=min(low[x],low[y])呢?
再讀一遍第二個point:low[i]表示 結點i及其子樹的結點 通過反向邊 能到達的結點的最小dfn值。
因為y不是x的子結點,因此這裡只能用low[x]=min(low[x],dfn[y])。