zoj 1119 / poj 1523 SPF (典型例题 求割点 Tarjan 算法)
poj : http://poj.org/problem?id=1523
如果无向图中一个点 u 为割点 则u 或者是具有两个及以上子女的深度优先生成树的根,或者虽然不是一个根,但是它有一个子女 w, 使得low[w] >= dfn[u];
其中low[u] 是指点 u 通过回边所能达到的 最小深度优先数,dfn[u]是指 点u 当前所处的 深度优先数;
low[u] 是在递归回退时计算出来的,dfn[u] 是在递归时直接计算的。
low[u] = min
{
dfn[u];
min{ low[w] } // w是u的一个子女
min{ dfn[v] } // v与u邻接, 且(u,v)是一条回边
}
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
1 // File Name :poj1523.cpp 2 // Author :Freetion 3 // Created Time :2013年09月11日 星期三 22时40分42秒 4 5 #define LOCAL //Please annotate this line when you submit 6 /********************************************************/ 7 #include <iostream> 8 #include <stdio.h> 9 #include <math.h> 10 #include <algorithm> 11 #include <string.h> 12 #include <string> 13 #include <map> 14 #define CLE(name) memset(name, 0, sizeof(name)) 15 using namespace std; 16 17 const int max_n = 1001; 18 int edge[max_n][max_n]; //边的邻接矩阵 19 int vis[max_n];//顶点访问状态 20 int node; //记录最大的节点号 21 int tmpdfn; // 在dfs中记录当前深度优先数 22 int dfn[max_n]; // 记录每个顶点的深度优先数 23 int low[max_n]; // 记录每个顶点通过回边或邻边所能达到的最低深度优先数 24 int son; // 根的子女数 25 int subnet[max_n]; // 记录每个节点(去掉该节点后)的连通分量个数 26 27 //深度优先搜索,求每个节点的low值(根据low值判断是否为割点) 28 void dfs(int u) 29 { 30 for (int v = 1; v <= node; v ++) 31 { 32 if (edge[u][v]) // 边存在 33 { 34 if (!vis[v]) // 没有被访问过,则v是u的儿子节点 35 { 36 vis[v] = 1; //标记 37 dfn[v] = low[v] = ++ tmpdfn; //dfs值 和 初始的low值 38 dfs(v); // 递归 39 low[u] = min(low[u], low[v]); //求low的第二种情况 40 if (low[v] >= dfn[u]) 41 { 42 if (u != 1) 43 subnet[u] ++; 44 else son ++; 45 //因为跟节点没有进入的边,只有出的边所以需要单独处理一下 46 } 47 } 48 else low[u] = min(low[u], dfn[v]); // 求low第三种情况的 49 } 50 } 51 } 52 53 int main() 54 { 55 int i, u, v, find, num = 1; 56 while (~scanf ("%d", &u) && u) 57 { 58 CLE(edge); 59 node = 0; 60 scanf ("%d", &v); 61 if (u > node) node = u; 62 if (v > node) node = v; 63 edge[u][v] = edge[v][u] = 1; 64 while (scanf ("%d", &u) && u) 65 { 66 scanf ("%d", &v); 67 if (u > node) 68 node = u; 69 if (v > node) 70 node = v; 71 edge[u][v] = edge[v][u] = 1;//标记边存在 72 } 73 //以下是初始化 74 dfn[1] = low[1] = tmpdfn = 1; 75 son = 0; 76 CLE(vis); CLE(subnet); 77 vis[1] = 1; 78 //以上是初始化 79 dfs(1); // 默认根节点为 1; 80 if (son > 1) //单独处理根 81 subnet[1] = son -1; 82 find = 0; 83 if (num > 1) 84 puts(""); 85 printf ("Network #%d\n", num ++); 86 for (int i = 1; i <= node; i ++) 87 { 88 if (subnet[i]) 89 { 90 find = 1; 91 printf (" SPF node %d leaves %d subnets\n", i, subnet[i] +1); 92 //因为有一条入边所以还要加上一个 1; 93 } 94 } 95 if (find == 0) 96 printf (" No SPF nodes\n"); 97 } 98 return 0; 99 }