UVA315---Network(连通性问题:求割点)

来源:互联网 发布:手机淘宝怎么注销不了 编辑:程序博客网 时间:2024/05/22 06:42

题目来源:https://vjudge.net/problem/UVA-315

题意

存在n个电话公司的网络连接站点,每个站点之间相互连通,是一个连通图,问,如果去掉一个站点,能够将整个网络体系变得不再连通,那么这样的店有几个?

思路

在这里利用深搜去实现寻找割点,下面讲一下几种情况,一一对应代码说一下。
图一:
这里写图片描述
这里的图一共有三个割点,编号分别是2 ,3,4,而去掉1或5,整个网络体系依旧是一个连通图。
图二:
这里写图片描述
此时在图一上加了一条红线,也即是将2,4连通,那么此时3便不是割点了,因为去掉3之后,可以通过2->4将整个图连通。
如果用深搜的思想去写,那么需要一个起点,也可以称为根节点,最终,整个图都会被遍历一遍,假设在这里,设点1为根节点(谁都可以)(无向图),那么整个遍历顺序就是:1->2->3->4->5,这样标记了编号之后,深搜经过的边就会构成名叫:深搜优先生成树的树形结构(树形结构不会有环)。也即是每个点都有一个访问时刻(num数组),那么分析一下存在割点的情况:
第一种:假设当前点围割点,那么访问时刻比它大的点与访问时刻比他小的点之间没有联系。
对于这一种情况,图一中的点3符合,而图二的点3不符合,明显看到2-4有条线跳过了3,那么我们该怎么判断当前点有没有被跳过呢?(点的编号越小,越靠近根哦)
主要还是那条红线,这条线并没有出现在深搜优先生成树中,所以叫做:反祖边(回退边),也就是能够从先序编号较大的点直接回到编号较小的点,所以根据这条红线,弄了一个数组low,low[4]=min(low[4],num[2]),所以由这个数组记录可以知道,4最高是可以回到点2的,所以去掉点3无所谓,所以在整个代码中,先序编号(num数组)记录了是整个深度优先生成树,他是连通的,而low数组记录的是当前点可以返回到编号最小的那个点的先序编号,比较的时候呢,我们可以比较low数组,比如:low[3]>low[4],可知,点4可以比3更接近根,所以可以跳过3,也就是说,去掉3,4依旧可以与比3更高的点相连接,使网络连通。
但是呢,难道我们真要一个一个比较吗?
图三:
这里写图片描述
那么,就像图三一样,如果真要一个个比较的话,也要比较low[3]与low[5]的关系,如果3的子节点有很多,那完了,铁定超时,所以我们可以直接把点3的所有子节点的low最小值赋给low[3],那样,当深搜回溯的时候,遇到3,如果此时low[3]是1(low[5]给的),那么和他自身的先序编号num[3]进行比较,说明他的子节点low的值有比它小的,也就是他的子节点与3上面的点有条边叫做反祖边(回退边),那说明3肯定不是割点。至于怎么赋值,可以在回溯的时候赋值呀。
但是呢,除此之外,还要特判这个点不是根节点,因为没有编号比根节点更小的了。。。你说是吧。。。
所以根节点还需要特判,怎么特判呢?
既然是树形结构,那么不存在环,所以当根节点的儿子数量大于1的时候,就说明他是割点了,如图:
这里写图片描述
对了,在图三里,5可不是1的儿子哦,因为那条绿色的线可没有参与组成深度优先生成树,他叫反祖边(回退边)。
至于代码里parent数组是做什么的,可以自行思考哦。

参考文章:http://www.cnblogs.com/llhthinker/p/4954082.html

代码

#include<cstdio>#include<vector>#include<cstring>#include<algorithm>using namespace std;vector<int> graph[110];//vector型邻接表存图int low[110],parent[110],num[110];bool vis[110];//标记是否访问过(形成树的过程)bool check[110];//当前这个点是否已经计算过(割点int number,ans;//number是编号void find_tree(int x){    low[x]=num[x]=number++;//赋初值,low和num一样    vis[x]=true;//标记已经访问过    int tmp=0;//为了验证根节点有几个儿子  0.0    for(int i=0; i<graph[x].size(); i++)    {        if(!vis[graph[x][i]])        {            parent[graph[x][i]]=x;            tmp++;            find_tree(graph[x][i]);            if(low[graph[x][i]]>=num[x]&&x!=1&&!check[x])            {                 ans++;check[x]=true;            }            else if(tmp>1&&x==1&&!check[x])            {                ans++;                check[x]=true;            }            low[x]=min(low[x],low[graph[x][i]]);//把x的low更新成包括他本身和他所有子节点的最小low值        }        else if(parent[x]!=graph[x][i])//反祖边        {            low[x]=min(low[x], num[graph[x][i]]);//把它赋成他的编号        }    }}int main(){    int n;    while(~scanf("%d",&n)&&n)    {        memset(low,0,sizeof(low));        memset(parent,0,sizeof(parent));        memset(num,0,sizeof(num));        int st,ed;        for(int i=1;i<=n;i++)        {            graph[i].clear();        }        while(~scanf("%d",&st)&&st)        {            while(getchar()!='\n')            {                scanf("%d",&ed);                graph[st].push_back(ed);                graph[ed].push_back(st);            }        }        memset(vis,0,sizeof(vis));        memset(check,0,sizeof(check));        number=0;        ans=0;        find_tree(1);        printf("%d\n",ans);    }}