一.原题链接:http://poj.org/problem?id=1523
二.题目大意:给一个网络,节点与节点相连,让你求去掉某些节点能不能使得整个网络不联通。
三.解题思路:就是求关节点。(去掉使得图不联通的点)
1.直接DFS,枚举删除每个点,看需要dfs多少次才能把所有点遍历完,注意点标号不连续。居然也是0ms。时间复杂度O(n^3),邻接表其实也优化不了稠密图。
2.Tarjan算法,其实就是记忆化搜索的思路。
原理:对于一个图,对它进行深度优先搜索之后,会产生一颗深度优先搜索树(其实就是搜索的路径)。
节点是关节点的充要条件是:
1.它是树的根节点并有2个以上子女。(很明显如果这个点没了,树左右2遍就断开了,记得是搜索树的子女,不是该节点有几个邻接点,邻接点不一定是搜索树的子女)
2.它不是根节点,但是以它出发向下的路径,没有节点能够返回在当前节点之前的点。
第一点好判断,第二点怎么判断呢?
我们引入一个dfn[]数组记录搜索的次序,祖先一定大于子女,
然后再引入一个low[]数组,(作用为记录其可以直接或间接访问的最上层的顶点)使其为本身的dfn[]、能通过当前节点返回其祖先或其祖先之上的节点的dfn[]和他子女的low[]的最小值,这样就能记录整条路径有没有回路了。
于是条件2转化为u不是根节点,并且存在子女v,使得low[v]>=dfn[u],只要看子女就行,因为low[]记录了整条路径的最小值。并且有多少个子女满足就分成多少块。
整个过程在DFS中进行,利用回溯进行low值的确定实在是太精妙了。时间复杂度网上很多都说O(n+m),顶点数+边数,因为他们认为扫所有的点,扫所有的边。我觉得这忽略了从一个点出发,扫其余点时候所需要的判断,稠密图邻接表也没用,不过这题数据很弱。
这题我用的tarjan其实是假设在搜索树的每个顶点都有一条回边回到他的直接祖先。(都有重边)不然的话我就要标记每条边,看看是否走过,因为不管是邻接表还是邻接矩阵,都无法确定是通过原来这条边访问节点的呢,还是有一条成环的边。就是我假设如下图:
这并不影响算法的正确性,因为2个点本来就相连,我加边或者不加边,一个点失去之后,该断的还是会断。但是如果求割边就不一样的。
网上许多代码都是这样的,只要是没标记边的,不信你手动模拟tarjan算法,然后打印每个low[]值看看。
四.代码:
1.DFS(0ms)
#include <cstdio> #include <iostream> #include <cstring> #include <algorithm> #include <queue> #include <vector> using namespace std; const int MAX_SIZE = 1002, INF = 0x3f3f3f3f; struct elem { int id, cnt; }; vector <int> G[MAX_SIZE]; vector <elem> SPF; bool visited[MAX_SIZE], isNode[MAX_SIZE]; int nodeNum = 0, del; void addEdge(int u, int v) { G[u].push_back(v); G[v].push_back(u); nodeNum = max(nodeNum, max(u, v)); isNode[u] = isNode[v] = true; } void init() { int i; for(i = 1; i <= 1000; i++) G[i].clear(); SPF.clear(); memset(isNode, 0, sizeof(isNode)); } void dfs(int u) { visited[u] = true; int i, v; for(i = 0; i < G[u].size(); i++){ v = G[u][i]; if(v != del && !visited[v]){ dfs(v); } } } int main() { //freopen("in.txt", "r", stdin); int u, v, i, j, kase = 1, cnt; while(1){ init(); scanf("%d", &u); if(0 == u) break; scanf("%d", &v); addEdge(u, v); while(1){ scanf("%d", &u); if(0 == u) break; scanf("%d", &v); addEdge(u, v); } for(i = 1; i <= nodeNum; i++){ if(!isNode[i]) continue; del = i; memset(visited, 0, sizeof(visited)); cnt = 0; for(j = 1; j <= nodeNum; j++){ if(j != del && !visited[j] && isNode[j]){ dfs(j); cnt++; } } if(cnt > 1){ elem tmp; tmp.id = del, tmp.cnt = cnt; SPF.push_back(tmp); } } printf("Network #%d\n", kase++); if(0 == SPF.size()) printf(" No SPF nodes\n"); else for(i = 0; i < SPF.size(); i++) printf(" SPF node %d leaves %d subnets\n", SPF[i].id, SPF[i].cnt); printf("\n"); } }
2.Tarjan
#include <cstdio> #include <iostream> #include <cstring> #include <algorithm> #include <queue> #include <vector> using namespace std; const int MAX_SIZE = 1002, INF = 0x3f3f3f3f; class Tarjan { public: vector <int> edges[MAX_SIZE]; int low[MAX_SIZE], dfn[MAX_SIZE], root, maxNode, subnetsNum[MAX_SIZE]; bool visited[MAX_SIZE], isNode[MAX_SIZE], isSPF[MAX_SIZE], found; void init() { int i; for(i = 0; i < MAX_SIZE; i++) edges[i].clear(); found = false; memset(subnetsNum, 0, sizeof(subnetsNum)); memset(isNode, 0, sizeof(isNode)); memset(visited, 0, sizeof(visited)); memset(isSPF, 0, sizeof(isSPF)); maxNode = 0; } void addEdge(int u, int v) { edges[u].push_back(v); edges[v].push_back(u); maxNode = max(maxNode, max(u, v)); isNode[u] = isNode[v] = true; } void dfs(int u, int depth) { visited[u] = true; low[u] = dfn[u] = depth; int i, v; for(i = 0; i < edges[u].size(); i++){ v = edges[u][i]; if(!visited[v]){ if(u == root) subnetsNum[root]++; dfs(v, depth + 1); if(u != root) low[u] = min(low[u], low[v]); if(low[v] >= dfn[u] && u!= root){ isSPF[u] = true; subnetsNum[u]++; found = true; } } else low[u] = min(low[u], dfn[v]); } } void startDFS() { for(root = 1; root <= maxNode; root++) if(isNode[root]) break; dfs(root, 1); if(subnetsNum[root] > 1){ isSPF[root] = true; subnetsNum[root]--; found = true; } } }G; int main() { //freopen("in.txt", "r", stdin); int u, v, i, j, kase = 1, cnt; while(1){ G.init(); scanf("%d", &u); if(0 == u) break; scanf("%d", &v); G.addEdge(u, v); while(1){ scanf("%d", &u); if(0 == u) break; scanf("%d", &v); G.addEdge(u, v); } G.startDFS(); printf("Network #%d\n", kase++); if(!G.found) printf(" No SPF nodes\n"); else for(i = 1; i <= G.maxNode; i++) if(G.isSPF[i]) printf(" SPF node %d leaves %d subnets\n", i, G.subnetsNum[i]+1); printf("\n"); } }