题目大意:给定一个网络,看是否有关节点存在(即割点,即去除该点后,图变成非连通图),如果存在输出该节点和去除该节点后连通子图的个数,如果不存在割点即输出没有割点存在。
求割点直接用dfs解决即可,问题在于统计去除该割点后,连通子图的个数,其实在dfs时,遇到割点时,只要发现其儿子结点的low值比该割点的dfn值大或相等,那么去除该结点后,其儿子结点能到达的所有结点 必定构成一个连通子图,这样用一个son数组记录所有这样的儿子结点的个数,最后加上割点的父亲结点能到达的所有结点形成的连通子图,即为去除该结点后整个图的连通子图的个数。
代码如下:
#include<iostream>
#include<cstring>
#include<cstdio>
#include<vector>
using namespace std;
const int MAXN = 1100;
int ROOT = 1;
#define MIN(a,b) a>b?b:a
#define MAX(a,b) a>b?a:b
int vis[MAXN],cut[MAXN],low[MAXN],dfn[MAXN],son[MAXN],p[MAXN],used[MAXN];
vector<int> g[MAXN];
void init()
{
memset(vis,0,sizeof(vis));
memset(cut,0,sizeof(cut));
memset(son,0,sizeof(son));
memset(used,0,sizeof(used));
for(int i=0;i<MAXN;i++)g[i].clear(),p[i]=i;
}
int find(int x)
{
return x==p[x]?x:p[x]=find(p[x]);
}
void merge(int x,int y)
{
int r1 = find(x),r2 = find(y);
if(r1==r2)return;
p[r1]=r2;
}
int conn()
{
int cnt =0;
for(int i=0;i<MAXN;i++)
{
if(p[i]==i&&used[i])cnt++;
}
return cnt;
}
void dfs(int u,int father,int deep)
{
vis[u] = 1;
dfn[u] = low[u] = deep;
int s = 0,record = 0;
for(int i=0;i<g[u].size();i++)
{
int v = g[u][i];
if(!vis[v])
{
dfs(v,u,deep+1);
s++;
low[u] = MIN(low[u],low[v]);
if((u==ROOT&&s>=2)||(u!=ROOT&&low[v]>=dfn[u]))
{
cut[u] = 1;record++;
}
}
else if(v!=father)
{
low[u] = MIN(low[u],dfn[v]);
}
}
son[u] = record;
}
int main()
{
int u,v;init();int cas=0,start=1;
while(scanf("%d",&u))
{
if(u!=0)
{
start = 0;
scanf("%d",&v);
g[u].push_back(v);
g[v].push_back(u);
used[u] = used[v] = 1;
merge(u,v);
continue;
}
if(u==0&&start)break;
for(int i=0;i<MAXN;i++)
{
if(!vis[i]&&used[i])
{
ROOT = i;
dfs(ROOT,-1,0);
}
}
bool flag = false;int cnt = conn();
printf("Network #%d\n",++cas);
for(int i=0;i<MAXN;i++)
{
if(cut[i])
{
flag = true;
printf(" SPF node %d leaves %d subnets\n",i,son[i]+cnt);
}
}
if(!flag)
{
printf(" No SPF nodes\n");
}
printf("\n");
init();start = 1;
}
return 0;
}