题目大意:给定一个网络,看是否有关节点存在(即割点,即去除该点后,图变成非连通图),如果存在输出该节点和去除该节点后连通子图的个数,如果不存在割点即输出没有割点存在。
求割点直接用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; }