Tarjan算法求桥和割点

来源:互联网 发布:工程算量软件 编辑:程序博客网 时间:2024/06/04 18:43

预备定义:

low[u]定义为u或者u的子树中能够通过非父子边追溯到的最早的节点的DFS开始时间

d[u]表示dfs过程中u的进栈时间

割点:无向连通图中,如果删除某点后,图变成不连通,则称该点为割点。

桥:无向连通图中,如果删除某边后,图变成不连通,则称该边为桥。



判断割点方法:

(1) u为树根,且u有多于一个子树。
(2) u不为树根,且存在(u,v)为树枝边(或称父子边,即u为v在搜索树中的父亲),使得dfn(u)<=low(v)。
   也就是u的子树中的v点无法到达u之前的点,所以u点去掉就是两个连通分支,所以u为割点


判断桥的方法:

一条边(u,v)是桥,当且仅当(u,v)为树枝边(即非负边),且满足dfn(u)<low(v)(前提是其没有重边)。

也就是,u的儿子v之间只有一条边(前提是无重边),且v点只能到v点到不了v点前,所以(u,v)边去掉就是两个连通分支,所以(u,v)为桥

注意:找桥的时候,要注意看有没有重边。有重边,则不是桥。


例题:POJ 1253 SPF

#include <algorithm>
#include <iostream>
#include <cstring>
#include <cstdio>
using namespace std;


int u,v,Case,times,flag,edgenum,col[10005],vet[10005],head[10005],nnext[10005],d[10005],low[10005],cuts[10005];


void init()
{
    flag=1;
    edgenum=0;
    for (int i=0;i<=1005;i++)
        col[i]=low[i]=d[i]=cuts[i]=vet[i]=nnext[i]=0,head[i]=-1;
    times=1;
}
void Addedge(int u,int v)
{
    vet[++edgenum]=v;
    nnext[edgenum]=head[u];
    head[u]=edgenum;
}
int Tarjan_CutPoint(int u)
{
    low[u]=d[u]=times++;
    for (int e=head[u],v;~e;e=nnext[e])
    {
        v=vet[e];
        if (!d[v])
        {
            Tarjan_CutPoint(v);
            low[u]=min(low[u],low[v]);
            if (low[v]>=d[u])
                cuts[u]++;
        }
        else
            low[u]=min(low[u],d[v]);
    }
    return 0;
}
int main()
{
//  freopen("spf.in","r",stdin);
// freopen("spf.out","w",stdout);
ios::sync_with_stdio(false);
    while (cin >> u,u)
    {
        init();
        cin >> v;
        Addedge(u,v);
        Addedge(v,u);
        while (cin >> u,u)
        {
            cin >> v;
            Addedge(u,v);
            Addedge(v,u);
        }
        for (int i=1;i<1001;i++)
        {
            if (head[i]==-1) 
                break;
            if (!d[i]) 
                Tarjan_CutPoint(i);
        }
        cout << "Network #" << ++Case << endl;      
        for (int i=1;i<1001;i++)
            if  (i==1)
            {
                if  (cuts[i]>=2)
                    cout << "  SPF node 1 leaves " << cuts[1] << " subnets" << endl,flag=0;
            }
            else
                if  (cuts[i])
                {
                    flag=0;
                    cout << "  SPF node " << i << " leaves " << cuts[i]+1 << " subnets" << endl;
                }
        if  (flag)
            cout << "  No SPF nodes" << endl;
        cout << endl;
    }
    cout << endl;
    return 0;
}


求桥的代码(也可以和上面的合并,毕竟判断条件差不多)

#include <algorithm>
#include <iostream>
#include <cstring>
#include <cstdio>
#include <vector>
using namespace std;


int n,m,times,bridge,edgenum,head[10005],nnext[10005],cut[10005],vet[10005],low[10005],d[10005],cuts[10005];


void init()
{
edgenum=0;
memset(head,-1,sizeof(head));
}
void Addedge(int u,int v)
{
vet[++edgenum]=v;
nnext[edgenum]=head[u];
head[u]=edgenum;
cuts[edgenum]=0;
}
void Tarjan(int u,int fa)
{
int v;
low[u]=d[u]=++times;
int son=0;
for(int e=head[u];e!=-1;e=nnext[e])
{
v=vet[e];
if (v==fa)
continue;
if (!d[v])
{
son++;
Tarjan(v,u);
low[u]=min(low[u],low[v]);
if (low[v]>d[u])
{
bridge++;
cuts[e]=1;
cuts[e^1]=1;
}
if (u!=fa && low[v]>=d[u])
cut[u]=1;
}
else if (low[u]>d[v])
low[u]=d[v];
}
if (u==fa && son>1)
cut[u]=1;
}


int main()
{
froepen("bridge.in","r",stdin);
freopen("bridge.out","w",stdout);
ios::sync_with_stdio(false);
cin >> n >> m;//点数和边数
init();
for(int i=1,u,v;i<=m;i++)
{
cin >> u >> v;
Addedge(u,v);
Addedge(v,u);
}
for(int i=1;i<=n;i++)
if (!d[i])
Tarjan(i,i);
vector< pair<int,int> >  ans;
for(int u=1;u<=n;u++)
for(int e=head[u];e!=-1;e=nnext[e])
if (cuts[e] && vet[e]>u)
ans.push_back(make_pair(u,vet[e]));
sort(ans.begin(),ans.end());
for(int i=0;i<ans.size();i++)
cout << ans[i].first << " " << ans[i].second << endl;
return 0;
}

原创粉丝点击