题目大意:求一个无向图的割点,并把割点连接的连通个数求出来
解题思路:tarjan算法求割点
这里定义:
Low(u)为u或u的子树中能通过非父子边追溯到的最早的节点,即DFS序号最小的节点。根据定义,则有:
Low(u)=Min
{
DFS(u)
DFS(v) (u,v)为后向边(返祖边) 等价于 DFS(v)<DFS(u)且v不为u的父亲节点
Low(v) (u,v)为树枝边(父子边)
}
满足割点的条件有:
一个顶点u是割点,当且仅当满足(1)或(2)
(1) u为树根,且u有多于一个子树。
(2) u不为树根,且满足存在(u,v)为树枝边(或称父子边,即u为v在搜索树中的父亲),使得DFS(u)<=Low(v)。
所以用一个cnt[i]保存i点连接的连通分量个数
由于0作为根节点,所以cnt[0] = 0;
其他节点初始化为1
当dfs[u] <= low[v]时,(u,v)为树枝边,cnt[u]++
#include <iostream>
#include <cstdio>
#include <cstring>
#include <algorithm>
using namespace std;
struct node
{
int v, next;
};
const int maxn = 1010;
int head[maxn], e, index, num, cnt[maxn], dfn[maxn], low[maxn];
node edge[maxn * maxn];
void addEdge(int u, int v);
void init();
void tarjan(int u);
int main()
{
int u, v, t = 0;
init();
while(scanf("%d", &u) != EOF && u != 0)
{
t++;
scanf("%d", &v);
u--; v--;
addEdge(u, v);
num = max(num, max(u, v));
while(scanf("%d", &u) != EOF && u != 0)
{
scanf("%d", &v);
num = max(num, max(u, v));
u--; v--;
addEdge(u, v);
}
cnt[0] = 0;
for(int i = 1; i < num; i++)
cnt[i] = 1;
tarjan(0);
printf("Network #%d\n", t);
bool flag = false;
for(int i = 0; i < num; i++)
{
if(cnt[i] > 1)
{
flag = true;
printf(" SPF node %d leaves %d subnets\n", i + 1, cnt[i]);
}
}
if(!flag)
printf(" No SPF nodes\n");
printf("\n");
init();
}
return 0;
}
void addEdge(int u, int v)
{
edge[e].v = v;
edge[e].next = head[u];
head[u] = e++;
edge[e].v = u;
edge[e].next = head[v];
head[v] = e++;
}
void init()
{
memset(head, -1, sizeof(head));
memset(dfn, -1, sizeof(dfn));
e = 0;
index = 0;
num = 0;
}
void tarjan(int u)
{
dfn[u] = low[u] = index++;
for(int i = head[u]; i != -1; i = edge[i].next)
{
int v = edge[i].v;
if(dfn[v] == -1)
{
tarjan(v);
if(dfn[u] <= low[v])
cnt[u]++;
else
low[u] = min(low[u], low[v]);
}
else
low[u] = min(low[u], dfn[v]);
}
}