1. 程式人生 > >求連通圖的割點(關節點)問題

求連通圖的割點(關節點)問題

割點的定義是,如果除去此節點和與其相關的邊,圖不再連通。

分析:

1. 最簡單也是最直接的演算法是,刪除一個點然後判斷連通性,如果刪除此點,圖不再連通,則此點是割點,反之不是割點(圖的連通性一般通過深搜來判定,是否能一次搜尋完 全部頂點);該方法複雜度較高為O(n^3)。當然具體實現並不真正去掉每個頂點(及其關聯邊),只需跳過該點就可以了。

2. 通過深搜優先生成樹來判定。從任一點出發深度優先遍歷得到優先生成樹,對於樹中任一頂點u而言,其孩子節點為鄰接點。

頂點u是割點的充要條件:

1)如果頂點u是深度優先搜尋生成樹的根,則u至少有2個子女。

2)如果u不是生成樹的根,則它至少有一個子女w,從w出發,不可能通過w、w的子孫,以及一條回邊組成的路徑到達u的祖先。

因此,對每個頂點u定義一個low值:low[u]是從u或u的子孫出發通過回邊可以到達的最低深度優先數。low[u]定義如下:

low[u]=min{dnf[u],min{low[w]|w是u的一個子女},min{dnf[v]|v與u鄰接,且(u,v)是一條回邊}}

即low[u]取三者的最小值,其中第一項為他本身的深度優先數;第二項為他的子女頂點w的low[w]的最小值,因為它的子女可以到達的最低深度優先數,則它也可以通過子女到達。

第三項為它直接通過回邊到達的最低深度優先數。

頂點u是割點的充要條件:頂點u是深度優先搜尋生成樹的根,則u至少有2個子女或不是根,但他有一個子女w,使得low[w]>=dnf[u].

看題:POJ 1532  SPF

Description
Consider the two networks shown below. Assuming that data moves around these networks only between directly connected nodes on a peer-to-peer basis, a failure of a single node, 3, in the network on the left would prevent some of the still available nodes from communicating with each other. Nodes 1 and 2 could still communicate with each other as could nodes 4 and 5, but communication between any other pairs of nodes would no longer be possible.

Node 3 is therefore a Single Point of Failure (SPF) for this network. Strictly, an SPF will be defined as any node that, if unavailable, would prevent at least one pair of available nodes from being able to communicate on what was previously a fully connected network. Note that the network on the right has no such node; there is no SPF in the network. At least two machines must fail before there are any pairs of available nodes which cannot communicate. 

                              

Input
The input will contain the description of several networks. A network description will consist of pairs of integers, one pair per line, that identify connected nodes. Ordering of the pairs is irrelevant; 1 2 and 2 1 specify the same connection. All node numbers will range from 1 to 1000. A line containing a single zero ends the list of connected nodes. An empty network description flags the end of the input. Blank lines in the input file should be ignored.
Output
For each network in the input, you will output its number in the file, followed by a list of any SPF nodes that exist.

The first network in the file should be identified as "Network #1", the second as "Network #2", etc. For each SPF node, output a line, formatted as shown in the examples below, that identifies the node and the number of fully connected subnets that remain when that node fails. If the network has no SPF nodes, simply output the text "No SPF nodes" instead of a list of SPF nodes.
Sample Input
1 2
5 4
3 1
3 2
3 4
3 5
0

1 2
2 3
3 4
4 5
5 1
0

1 2
2 3
3 4
4 6
6 3
2 5
5 1
0

0
Sample Output
Network #1
SPF node 3 leaves 2 subnets

Network #2
No SPF nodes

Network #3
SPF node 2 leaves 2 subnets
SPF node 3 leaves 2 subnets

題意就是求無向連通圖的割點,並求去掉該割點後分為幾個聯通圖。

#include<cstdio>
#include<cstring>
#define min(a,b) ((a)<(b)?(a):(b))
int Edge[1001][1001];///鄰接矩陣
int visited[1001];///頂點訪問狀態
int nodes;///頂點數目
int tmpdnf;///在dfs中記錄當前深度優先搜尋序數
int dnf[1001];
int low[1001];///根據該值來判斷是否為關節點
int son;///根節點子女個數(如果根節點子女個數大於等於2,根節點是關節點)
int subnets[1001];///記錄每個節點去掉該節點後連通分量的個數
void dfs(int u)
{
    for(int v=1;v<=nodes;v++)
    {///v跟u鄰接在生成樹中就是2種情況
        ///(1)v是u的祖先,這樣(v,u)就是一條回邊.(2)v是u的兒子節點
        if(Edge[u][v])
        {
            if(!visited[v])///v還未訪問,v是u的兒子節點
            {
                visited[v]=1;
                tmpdnf++;
                dnf[v]=low[v]=tmpdnf;
                dfs(v);///執行完後,low[v]值以求出
                low[u]=min(low[u],low[v]);///回退時計算u的low值
                if(low[v]>=dnf[u])
                {
                    if(u!=1)
                        subnets[u]++;///去掉該節點後連通分量的個數
                    if(u==1)///根節點子女個數(如果根節點子女個數大於等於2,根節點是關節點)
                        son++;
                }
            }
            else///v是u的祖先,這樣(v,u)就是一條回邊
                low[u]=min(low[u],dnf[v]);
        }
    }
}
void init()///初始化函式
{
    low[1]=dnf[1]=1;
    tmpdnf=1;
    son=0;
    memset(visited,0,sizeof(visited));
    visited[1]=1;
    memset(subnets,0,sizeof(subnets));
}
int main()
{
    int i;
    int u,v;///輸入的頂點對
    int find;///是否找到割點的標誌
    int number=1;
    while(1)
    {
        scanf("%d",&u);
        if(u==0)
            break;///整個輸入結束
            memset(Edge,0,sizeof(Edge));
            nodes=0;
        scanf("%d",&v);
        if(u>nodes)
            nodes=u;
        if(v>nodes)
            nodes=v;
        Edge[u][v]=Edge[v][u]=1;
        while(1)
        {
            scanf("%d",&u);
            if(u==0)
                break;///當前輸入結束
            scanf("%d",&v);
            if(nodes<u)
                nodes=u;
                if(nodes<v)
                    nodes=v;
                Edge[u][v]=Edge[v][u]=1;
        }
            printf("Network #%d\n",number++);
            init();
            dfs(1);
            if(son>1)
                subnets[1]=son-1;
            find=0;
            for(i=1;i<=nodes;i++)
            {
                if(subnets[i])
                {
                    find=1;
                    printf("  SPF node %d leaves %d subnets\n",i,subnets[i]+1);
                }
            }
            if(!find)
                printf("  No SPF nodes\n");
                printf("\n");
    }
}