对于任何一个网络,保证其稳定性都极其重要,如果在一个网络中有着一个点,使得这个点损坏了就会导致整个网络不连通,那么这个网络出现问题的风险就比较大,这个点我们称为割点。类似的有割边,即如果删除这个边,连通分量会增加。我们先来研究最基础的问题,一张无向连通图中割点的数量有几个。很明显,我们的第一反应是暴力做法,即每次删除一个节点,再dfs判断这张图的连通性,若删除之后不连通则为割点。我们发现这种做法的复杂度为O(v(v+e)),对于较大规模的图显得不够优秀,容易超时,于是我们来考虑另一种做法。

  我们可以先对图进行一次dfs,得到一棵深搜生成树,接下来,我们来研究这棵树的性质。

  1.对于根节点而言,如果它具有多棵子树,那么它一定是割点。因为从dfs的性质我们知道根节点的a子树一定不会有节点1连接到它的b子树的节点2上,假如有,那么节点2一定在子树a上,这就与前提矛盾了。所以,如果删除这个根节点,那么连通分量一定增加,故而是割点。

  2.对于非根节点a而言,如果它存在一个子节点b,使得这个子节点b以及以这个子节点b为根的子树都没有与a的祖先相连接的边,那么节点a为割点。这个性质同样可以由dfs的性质证明。

  那么,如何代码实现呢?

  首先,对于根节点,我们容易进行判断,只要计算子树数量即可。对于非根节点就有一些复杂了,我们需要判断这个节点的子节点及其后代有无回退边,我们可以使用dfn作为计时器,计算该节点何时被dfs到(以判断亲缘关系),并将该值赋值给num数组。再建立一个low数组,表示该节点及其后代的所能连接到的最近的祖先是哪个节点,如果是其父亲节点的祖先,那么其父亲节点不是割点,否则是割点。

例题 poj1144

 

Network
Time Limit: 1000MS   Memory Limit: 10000K
Total Submissions: 21824   Accepted: 9612

Description

A Telephone Line Company (TLC) is establishing a new telephone cable network. They are connecting several places numbered by integers from 1 to N . No two places have the same number. The lines are bidirectional and always connect together two places and in each place the lines end in a telephone exchange. There is one telephone exchange in each place. From each place it is
possible to reach through lines every other place, however it need not be a direct connection, it can go through several exchanges. From time to time the power supply fails at a place and then the exchange does not operate. The officials from TLC realized that in such a case it can happen that besides the fact that the place with the failure is unreachable, this can also cause that some other places cannot connect to each other. In such a case we will say the place (where the failure
occured) is critical. Now the officials are trying to write a program for finding the number of all such critical places. Help them.

Input

The input file consists of several blocks of lines. Each block describes one network. In the first line of each block there is the number of places N < 100. Each of the next at most N lines contains the number of a place followed by the numbers of some places to which there is a direct line from this place. These at most N lines completely describe the network, i.e., each direct connection of two places in the network is contained at least in one row. All numbers in one line are separated
by one space. Each block ends with a line containing just 0. The last block has only one line with N = 0;

Output

The output contains for each block except the last in the input file one line containing the number of critical places.

Sample Input

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

Sample Output

1
2

Hint

You need to determine the end of one line.In order to make it's easy to determine,there are no extra blank before the end of each line.
 
  题目大意就是求一个网络中有几个割点,是割点的模板题,数据很水,但是输入非常恶心人。
  代码如下
#include<iostream>//poj不支持万能头哦 
#include<cstdio>
#include<vector>
#include<cstring>
using namespace std;
const int maxn=105;
vector<int>g[maxn];
int dfn=0;
int num[maxn];
int low[maxn];
bool iscut[maxn];
int ans=0;
void dfs(int x,int fa){//fa为x的父节点 
    num[x]=low[x]=++dfn;//便于判断继承关系 
    int child=0;
    for(int i=0;i<g[x].size();i++){
        int v=g[x][i];
        if(!num[v]){//判断是否dfs过 
            child++;
            dfs(v,x);
            low[x]=min(low[x],low[v]);//更新该节点及后代的最近祖先 
            if(low[v]>=num[x]&&x!=1){//注意是非根节点 
                iscut[x]=1;
            }
        }
        else if(num[x]>num[v]&&v!=fa){//判断回退边,注意父亲节点可以回退到,但是不算 
            low[x]=min(low[x],num[v]);
        }
    }
    if(x==1&&child>=2){//如果是根节点并且有多子树则为割点 
        iscut[x]=1;
    }
}
int main(){
    ios::sync_with_stdio(false);
    cin.tie(0);
    cout.tie(0);
    int n;
    cin>>n;
    while(n){
        for(int i=1;i<=100;i++)g[i].clear();
        memset(num,0,sizeof(num));
        memset(low,0,sizeof(low));
        memset(iscut,0,sizeof(iscut));
        dfn=0,ans=0;//以上为初始化操作 
        string s;
        while(getline(cin,s)){//恶心的输入 
            s.push_back(' ');
            if(s[0]=='0')break;
            int x=0;
            int p;
            for(int i=0;i<s.size();i++){
                if(s[i]>='0'&&s[i]<='9')x=x*10+s[i]-'0';
                else{
                    p=i;
                    break;
                }
            }
            int y=0;
            for(int i=p+1;i<s.size();i++){
                if(s[i]>='0'&&s[i]<='9'){
                    y=y*10+s[i]-'0';
                }
                else{
                    g[x].push_back(y);//建无向图 
                    g[y].push_back(x);
                    y=0;
                }
            }
        }
        dfs(1,-1);//以1为根节点dfs 
        for(int i=1;i<=n;i++){
            if(iscut[i])ans++;//割点数 
        }
        cout<<ans<<endl;
        cin>>n;
    }
    return 0;
}

放假了,好久没有写博客了,摸一篇。

posted on 2021-08-22 20:09  雪之下雪乃天下第一  阅读(140)  评论(0编辑  收藏  举报