求割点 和 点联通分量

今天又开始搞图论,今天的这个算法是用来求割点的算法。以及将这个点去掉后可以形成的强联通分量数。
先来解释一下原理:
先来思考一下最朴素的算法,就是将每个点进行标记,意为将其去掉,然后进行深搜遍历,统计联通分量的个数。
这个时间复杂度为 n (这个是n个被去掉的点)* n (判断n个点)* n(搜索)???

这个宝宝也不是很清楚的拉 略略略~

先来说一下Tarjan(塔尔杨) 算法的原理,
假定从任意一点开始深搜,记录深搜的顺序,这样的话就会形成一颗深度优先搜索树,这时 观察边‘
就会发现边存在两种情况
一种是在生成树中的,另一种是不在生成树中的。
不在生成树中的边叫做回边,至于为什么叫它回边一会说。

在观察生成树树中的边,有这样的发现。

如果点u是割点,分两种情况,
一种是 u 是树根,这时就要求u至少有两个孩子,这样的话将u去掉的话,u有多少孩子,就会出现多少联通分支。

另一种情况就是 u 不是树根,这是 它至少有一个孩子w, 从w出发不可能通过w,及w的子孙和 回边到达u的祖先,

这时来解释一下 “回边” 这个词,为什么叫回,首先考虑为什么上面的情况说不能到达u
的祖先。

如果可以到达u的祖先的话这样当去掉u时,u的孩子w 可以通过 w的子孙 加上回边 连接到u 的祖先,当去掉u的时候 u 下面的部分还是可以通过回边连接在u 所在的联通分支上的,就相当于回去了又。

反之的话,u 的孩子w通过回边回到的点还是在u的子孙节点上的话,去掉u,这样u的子树就可以断开了。

以上的话就要知道 每个点的遍历顺序,和其能回到的点的最小值。不妨用low[] 来存储这个最小值。用dfn[]来存储每个点的点的遍历顺序
又可以发现low[u]有来自以下三个值

low[u] = MIN
{
    dfn[u];
    min(low[w],|w 是 u 的子女)
    min(dfn[v],|(u,v)是一条回边);
}

就是取这三个值的最小值。
总结一下的话,就是如果这个 是树根那样的话,必须有两个以上的孩子节点
如果u不是孩子节点,那样 low[u] >= dfn[u] 说明就算走回边 的话,也只能回到u的下面或者u本身。

来水个题 poj1523 这个题的背景也是联通分量应用的场景之一。

#include
#include
#include
using namespace std;
const int MAXN = 1e3 + 10;
int G[1001][1001];
bool tag[1001];
int nodes;
int temp;
int dfn[MAXN];
int low[MAXN];
int son; // 根节点子女节点个数
int subnet[MAXN];
void dfs(int u)
{
    for(int v = 1;v <= nodes;++v)
    {
        if(G[u][v]) // 两点之间可以联通
        {
            if(tag[v]) // 如果被访问过,这个又一次的被访问到说明是 "回边",那样v是u的祖先节点
            {
                low[u] = min(low[u],dfn[v]); // 这时的话就要判断回边可以回到的最小深度
            }
            else // 如果没有被访问过的话,说明这个点是u的儿子节点.
            {
                tag[v] = 1; // 对其标记
                temp++; dfn[v] = low[v] = temp ; // 给其标记深度值
                dfs(v); // 深度搜索

                low[u] = min(low[u],low[v]); //这是回退过程,更新low
                if(low[v] >= dfn[u]) // 如果
                {
                    if(u != 1) subnet[u]++; //如果不是根节点,去掉该点
                    if(u == 1) son++; // 如果是根节点,记录根节点的子女的个数
                }
            }
        }
    }
}
void init()
{
    low[1] = dfn[1] = 1;
    temp = 1;son = 0;
    memset(tag,0,sizeof(tag));
    tag[1] = 1;
    memset(subnet,0,sizeof(subnet));
}
int main()
{
    int i;
    int u,v;
    int flag;
    int num = 1;
    while(1)
    {
        cin >> u;
        if(u == 0) break;
        memset(G,0,sizeof(G));
        nodes = 0;
        cin >> v;
        nodes = max(u,v);
        G[u][v] = G[v][u] = 1;
        while(1)
        {
            cin >> u;
            if(!u) break;
            cin >> v;
            nodes = max(u,v);
            G[u][v] = G[v][u] = 1;
        }
        if(num > 1) cout << endl;
        cout << "Network #" << num++ << endl;
        init();
        dfs(1);
        if(son > 1) subnet[1] = son - 1;
        flag = 0;
        for(int i = 1;i <= nodes;++i)
        {
            if(subnet[i])
            {
                flag = 1;
                cout << " SPF node " << i << " leaves " << subnet[i] + 1 << " subnets" << endl;
            }
        }
        if(!flag) cout << " No SPF nodes" << endl;
    }
    return 0;
}

你可能感兴趣的:(图论及其应用)