zju 1119 SPF

   这个题让我做得有些纠结。将我的做题过程思路写出来。   这是一道深度优先搜索的题目,目的是来求出无线连通图的割点。首先,要明白什么是割点,割点:在一个连通图中,如果去掉了某个点和所有与这个点相连的边后,是图分成了两个部分,变成了一个不连通的图。那么这个点就是割点。还有,就是如何来求割点?

  搜索深度:如果节点k的搜索深度为j,则节点k为第j个搜索到的节点。

  若有k的儿子为i,我们定义AnceDeep[i]为与结点i相连接的所有节点最小的搜索深度,deep[k]为k的搜索深度(时间戳),那么k为割点当且仅当k满足(1)(2)中的一个:  

  (1) 若k为深搜树的根Root,当且仅当k的儿子数(分支数)>=2时k为割点;  

  (2) 若k为搜索树的中间结点(即k既不为根也不为叶),那么k必然有father和son,若AnceDeep[son]>= deep[k],则k必然为割点。  

   这样看起来并不好懂,我来引用一个学长写的文章来解释一下上面的(1)、(2)。 根据割点的定义,割点将图分成了两个部分,这个两个部分是靠割点来连通的,如果没有割点存在,那么图将不连通。所以,换句话说,我们要从第一部分的点进入第二部分的话,我们必须经过这个割点。然后接着,我们再考虑图的dfs的时候。由于割点是通往第二部分的门户,那么当我们从第一部分开始对图进行dfs的时候,如果访问了割点,那么再一次回退到割点的时候,必须是等第二部分的所有的点都访问完了之后才可以,否则就相当于“大门”被关闭了。第二部分的点就无法再次访问到了。因为dfs遇到访问过的点就立刻返回。

  对dfs来说,她遍历的最后是生成一棵树,其中树有一些叶子节点,形成叶子节点的原因是,他们能接触到的点都已经在他们之前访问过了。所以到了他们之后dfs不再继续递归了。联系到上面,我们可以看出dfs遍历所形成的树中的图的第二部分的点,与他们相连的只能是图中第二部分的点,或者是割点。所以我们如果对dfs访问的点按顺序来进行标号的话,那么第二部分的点的被访问序号一定要比割点的访问序号要大。

  所以我们就想到了利用这个特性,用dfs求割点的方法:

  首先从任意点开始,进行dfs,每递归到一个点,如果这点是没有被访问的,那么用dfs的序号给这个点的n1赋值,然后还要接着去访问与这个点相邻的其他的点,把其中最小的返回值返回,作为这个点的n2值。如果相连的点是被访问过的,那么直接把该店的n1作为返回值。然后,在访问的过程中,还要记录下序边,下序边是指,在生成的树中,由父节点指子节点的边。是有方向的。当dfs结束后。对于每一个点,从它的下序点中找到最小的n2的值,若果n1<=n2,那么就说明,他的下序点中,没有比它先被访问的点,那就说明这个点是一个割点。若果n1>n2,那么这个点不是割点。

 (但要注意,这个算法有一个默认的前提假设,就是说,dfs的起始点是被割点分成的两个部分中的点,但是如果这个起始点开始就是割点。则最后的比较是找不到割点的,所以最后我们还需要一个单独对这个起始点的判断,用最简单的dfs或者bfs即可)。

  解释清楚这些之后,然后就是写代码的过程了。我需要知道节点的数量N;需要用一个邻接矩阵M[][]来表示点与点之间的关系,并且在输入的过程中初始化;需要一个数组dfn[]来表示各个点的搜索深度,需要一个变量lbl随着访问节点数量的增加而自加并赋值给dfn[];需要一个数组low[]来表示节点所有子孙的祖先中最小的搜索深度;需要有一个数组ans[]来表示所有节点是不是割点,如果是割点,去掉这个割点之后分为几个连通分量了呢?这也可以用ans[]来表示。  

   那么该如何求N呢?在输入数据的过程中,最大的值就是N。如何求dfn[]呢?如果一个节点没有被访问到,那么这个节点的dfn值就是上一个访问的节点的dfn的值加1。如何来求low[r]呢?首先,对low[r]赋初值为dfn[r],在dfs的过程中,根据节点r的每个孩子节点的low[]值来更新自己的low[]值。low[r] = low[r]<low[i] ? low[r]:low[i];  

   大概就是这些,这个对我来说,真心不太好编。。唉!!我是一颗大白菜啊。。欢迎交流

 1 #include <iostream>
 2 #include <cstring>
 3 using namespace std;
 4 
 5 const int MAX_VERTEX = 1024;    // 图顶点个数最大值
 6 int T, v1, v2;    // 分别为测试数据组数和一条边的两个端点
 7 int N, M[MAX_VERTEX][MAX_VERTEX];    // 分别为图的顶点个数和图的邻接矩阵
 8 int dfn[MAX_VERTEX], lbl;    // 各顶点的dfn值:在DFS期间,根据顶点v被首次访问的次序指定整数dfn(v),即若dfn(v)=i,则v是第i个首次被访问的顶点,dfn(v)称为v的深度优先搜索序数
 9 int low[MAX_VERTEX];    // 各顶点的low函数值,表示顶点祖先中最小的dfn 
10 int ans[MAX_VERTEX];    // 结果,即ans[i]>0时说明顶点i是割点,且ans[i]+1为去掉顶点i后连通子网络的个数
11 
12 // DFS算法求割点函数
13 void SolveByDfs(int r);
14 
15 int main()
16 {
17     T = 0;    // 测试数据组数
18     while (cin>>v1 && v1!=0)    // 第一个端点不为零时,继续
19     {
20         cin >> v2;
21         
22         // 初始化
23         N = v1>v2 ? v1:v2;    // 图的顶点个数为v1和v2的最大值
24         memset(M, 0, sizeof(M));    // 图的邻接矩阵初始化为0
25         memset(dfn, 0, sizeof(dfn));
26         memset(low, 0, sizeof(low));
27         memset(ans, 0, sizeof(ans));
28         lbl = 0;
29 
30         // 读入数据,求解并输出结果
31         M[v1][v2] = M[v2][v1] = 1;
32        while (cin>>v1 && v1!=0)
33         {
34             cin >> v2;
35             N = N>v1 ? N:v1;
36             N = N>v2 ? N:v2;
37             M[v1][v2] = M[v2][v1] = 1;
38         }
39         SolveByDfs(1);
40         --ans[1];
41         ++T;
42         if (T > 1)
43         {
44             cout << endl;
45         }
46         cout << "Network #" << T << endl;
47         int ok = 1;
48         for (int i=1; i<=N; ++i)
49         {
50             if (ans[i] > 0)
51             {
52                 cout << "  SPF node " << i << " leaves " << ans[i] + 1 << " subnets" << endl;
53                 ok = 0;
54             }
55         }
56         if (ok == 1)
57         {
58             cout << "  No SPF nodes" << endl;
59         }
60     }
61 
62     return 0;
63 }
64 
65 void SolveByDfs(int r)
66 {
67     ++lbl;
68     low[r] = dfn[r] = lbl;    // 求得顶点r的dfn值并为low(r)赋初值为dfn(r)
69     for (int i=1; i<=N; ++i)
70     {
71         if (M[r][i] != 0)    // 顶点i是顶点r的邻接点
72         {
73             if (M[r][i] == 1)    // 边ri未检查
74             {
75                 M[r][i] = M[i][r] = 2;    // 标记边ri已检查
76                 if (dfn[i] == 0)    // 前向边,若i还未被访问过 
77                 {
78                     SolveByDfs(i);
79                     low[r] = low[r]<low[i] ? low[r]:low[i];
80                     if (low[i] >= dfn[r])
81                     {
82                         ++ans[r];
83                     }
84                 }
85                 else    // 后向边,i已经被访问过 
86                 {
87                     low[r] = low[r]<dfn[i] ? low[r] : dfn[i];
88                 }
89             }
90         }
91     }
92 }

 

 

转载于:https://www.cnblogs.com/wangaohui/archive/2012/11/18/2776394.html

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值