Zoj 1119 POJ 1523 SPF 求关节点及删除关节点会出现多少个连通分量 Tarjan算法

有最朴素的想法就是依次删除每个点,看剩下的图有多少个连通分量,如果只有一个,不是关节点(也叫割点),否则是关节点。

这种算法的复杂度是O(n^3).

这里的Tarjan算法的复杂度只有O(n^2),在连通图中只做一次DFS,根据DFS时遍历结点的先后顺序,依次给每个结点编号,这个编号叫该点的深度值,存在数组dfn[]中

求每个点的low[]值,low值是这样求的,每个点的low值最初就是该点的深度值。然后看该点的邻接点,如果该点的邻接点被访问过了,low值就是min(low[自己],dfn[邻接点])。

如果未被访问过,父结点的 low值=min(low[自己],low[子结点]),如果出现子结点的low值等于父结点的深度,那么说明该子结点最多只能到父结点,删掉父结点,子结点去不了父结点的祖先结点,换句话说,就是删掉父结点及与父结点相关联的边,这个图会不连通。

这么说吧,在DFS时,一个点如果是关节点,1:它是根结点,有>=2个孩子,那么删掉该点,图会不连通

2:它不是根结点,它的孩子结点除了通过它和它的祖先结点连通外没有其他的路径和它的祖先结点连通,那么显然删掉这个结点,图也不连通了。

贴代码:

View Code
 1 #include <cstdio>
 2 #include <cstring>
 3 #define MAXN 1005
 4 bool edge[MAXN][MAXN];//邻接矩阵记录图
 5 bool used[MAXN];
 6 int nodes;//最大结点数
 7 int subnets[MAXN];//subnets[i]表示去掉节点i后的连通分支数
 8 int dfn[MAXN];//DFS时该结点的深度
 9 int low[MAXN];//每个顶点的low值,用来判断是否是关节点
10 int tmpdfn;//DFS时编顶点的深度值
11 int son;
12 int min(int x,int y)
13 {
14     return x<y ? x:y;
15 }
16 void init()
17 {
18     low[1] = dfn[1] = 1;
19     tmpdfn = 1;
20     son = 0;
21     memset(used,0,sizeof(used));
22     used[1] = 1;
23     memset(subnets,0,sizeof(subnets));
24 }
25 void DFS(int u)
26 {
27     for(int v=1; v<=nodes; ++v)
28     {
29         if(edge[u][v])//v与u相邻
30         {
31             if(used[v]) low[u] = min(low[u],dfn[v]);//这是一条回边
32             else//v是儿子结点
33             {
34                 used[v] = 1;
35                 dfn[v] = low[v] = ++tmpdfn;
36                 DFS(v);
37                 low[u] = min(low[u],low[v]);//儿子结点的low值和自己的low值中选小的
38                 //儿子结点能到达哪里,父亲节点通过儿子结点到达。
39                 if(low[v] == dfn[u])
40                 {
41                     if(u == 1) son++;
42                     else subnets[u]++;
43                 }
44             }
45         }
46     }
47 }
48 void Tarjan()
49 {
50     bool flag = 0;
51     init();
52     DFS(1);
53     if(son > 1) subnets[1] = son-1;
54     for(int i=1; i<= nodes; ++i)
55     {
56         if(subnets[i])
57         {
58             flag = 1;
59             printf("  SPF node %d leaves %d subnets\n",i,subnets[i]+1);
60         }
61     }
62     if(!flag) printf("  No SPF nodes\n");
63 }
64 int main()
65 {
66 //    freopen("in.cpp","r",stdin);
67     int u,v,ser=0;
68     while(scanf("%d",&u))
69     {
70         if(u==0) break;
71         memset(edge,0,sizeof(edge));
72         nodes = 0;
73         scanf("%d",&v);
74         if(u>nodes) nodes = u;
75         if(v > nodes) nodes = v;
76         edge[u][v] = edge[v][u] = 1;
77         while(scanf("%d",&u))
78         {
79             if(u == 0) break;
80             scanf("%d",&v);
81             if(u>nodes) nodes = u;
82             if(v>nodes) nodes = v;
83             edge[u][v] = edge[v][u] = 1;
84         }
85         if(ser > 0) puts("");
86         printf("Network #%d\n",++ser);
87         Tarjan();
88     }
89     return 0;
90 }

 

转载于:https://www.cnblogs.com/allh123/archive/2013/04/10/3012981.html

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值