tarjan算法的應用。
還需多練習…….遇上題目還是容易傻住
對於tarjan算法中使用到的Dfn和Low數組.
low[u]:=min(low[u],dfn[v])——(u,v)為后向邊,v不是u的子樹;
low[u]:=min(low[u],low[v])——(u,v)為樹枝邊,v為u的子樹;
1.求割點:
割點:若刪掉某點后,原連通圖分裂為多個子圖,則稱該點為割點。
原理:若low[v]>=dfn[u],則u為割點。因low[v]>=dfn[u],則說明v通過子孫無法到達u的祖先。那么對於原圖,去掉u后,必然會分成兩個子圖。
所以處理節點u時,先遞歸v的子節點,然后回溯至u時,如果滿足low[v]>=dfn[u],則u為割點。
int tarjan(int x)
{
v[x]=1; //點的狀態標記,1為已訪問,2為割點
Dfn[x]=Low[x]=time++;
for(int i=head[x];i;i=next[i])
{
if(!v[ver[i]])
{
tarjan(ver[i]);
Low[x]=min(Low[x],Low[ver[i]]);
if(Dfn[x]<=low[ver[i]])
v[x]++;
}
else
Low[x]=min(low[x],Dfn[ver[i]]);
if((x==1&&v[x]>2)||(x>1&&v[x]>1)) //對第一個特判
v[x]=2;
else
v[x]=1;
}
}
2.求橋
橋(割邊):刪掉它之后,圖必然會分裂為兩個或兩個以上的子圖。
原理:若low[v]>dfn[u],則(u,v)為橋。由割點同理可得。但是由於可能存在重邊,需要把一條無向邊拆成的兩條標號相同的有向邊,記錄每個點的父親到它的邊的標號,如果邊(u,v)是v的父親邊,就不能用dfn[u]更新low[v]。這樣如果遍歷完v的所有子節點后,發現low[v]=dfn[v],說明u的父親邊(u,v)為割邊。
void tarjan(int x)
{
v[x]=1;
Dfn[x]=Low[x]=time++;
for(int i=head[x];i;i=next[i])
{
if(!v[ver[i]])
{
p[ver[i]]=edge[i]; //記錄父親邊
tarjan(ver[i]);
Low[x]=min(Low[x],Low[ver[i]]);
}
else if(p[x]!=edge[i]) //不是父親邊則更新
Low[x]=min(Low[x],Dfn[ver[i]]);
if(p[x]&&low[x]==dfn[x])
f[p[x]]=1; //為割邊
}
}
3.點雙連通分量
點雙連通分支,在求割點的過程中就能把每個點雙連通分支求出。建立一個棧,存儲雙連通分支。在搜索圖時,每找到一條樹枝邊或后向邊(非橫叉邊),就把這條邊加入棧中。如果遇到某時滿足DFS(u)<=Low(v),說明u是一個割點,同時把邊從棧頂一個個取出,直到遇到了邊(u,v),取出的這些邊與其關聯的點,組成一個點雙連通分支。
if(dfn[u]==low[u])
{
scc++;
while(1) //記錄每一個點屬於的連通塊
{
v=sta[top--];
instack[v]=0;
belong[v]=scc; //所取出的點即為雙連通分支
if(v==u)
break;
}
}
}
4.邊雙連通分支。在求出所有的橋以后,把橋邊刪除,原圖變成了多個連通塊,則每個連通塊就是一個邊雙連通分支。橋不屬於任何一個邊雙連通分支,其余的邊和每個頂點都屬於且只屬於一個邊雙連通分支。
5.一個有橋的連通圖,如何把它通過加邊變成邊雙連通圖
首先求出所有的橋,然后刪除這些橋邊,剩下的每個連通塊都是一個雙連通子圖。把每個雙連通子圖收縮為一個頂點,再把橋邊加回來,最后的這個圖一定是一棵樹,邊連通度為1。
統計出樹中度為1的節點的個數,即為葉節點的個數,記為leaf。則至少在樹上添加(leaf+1)/2條邊,就能使樹達到邊二連通,所以至少添加的邊數就是(leaf+1)/2。
void Tarjan(int u,int fa)
{
int i,v;
low[u]=dfn[u]=++cnt;
sta[++top]=u;
instack[u]=1;
for(i=first[u];i!=-1;i=edge[i].next)
{
v=edge[i].v;
if(i==(fa^1))
continue;
if(!dfn[v])
{
Tarjan(v,i);
low[u]=min(low[u],low[v]);
}
else if(instack[v])
low[u]=min(low[u],dfn[v]);
}
if(dfn[u]==low[u])
{
scc++;
while(1) //記錄每一個點屬於的連通塊
{
v=sta[top--];
instack[v]=0;
belong[v]=scc;
if(v==u)
break;
}
}
}
for(i=1;i<=n;i++)
{
for(j=first[i];j!=-1;j=edge[j].next)
{
v=edge[j].v;
if(belong[i]!=belong[v])
degree[belong[i]]++; //degree為1則為leaf
}
}
int sum=0;
for(i=1;i<=n;i++)
if(degree[i]==1)
sum++; 統計leaf數
}