POJ 1523 SPF【双连通分量_割点】
题意: 找出无向图的割点,并判断每个割点去掉后能形成多少个双连通分量。
分析: 割点的定义:
在深搜树中,时间戳为 dfn[k] ,当 K 满足(1)(2)中一个时,k 为割点
(1) k 为深搜树的根,当且仅当 k 的儿子个数>=2 时 k 为割点;
(2) k 为深搜树的中间节点(k 既不是根也不是叶),那么k 必然有父亲和儿子;
对于(1)是显然的,根结点k一旦有2个以上的分支,那么删除k必然出现森林;
对于(2)首先注意low[son]>= dfn[k]这个条件,意思就是“k的儿子son的辈分最高的祖先(暂且设其为w)的深度,比k的深度要深(或者等于k的深度,此 时k就是w),就是说k的辈分比w更高(深度更浅),那么一旦删除k,son所在的网络势必和 k的father所在的网络断开”,那么k就是割点。
code :
#include<stdio.h> #include<string.h> #define min(a,b)(a)<(b)?(a):(b) #define max(a,b)(a)>(b)?(a):(b) #define clr(x)memset(x,0,sizeof(x)) const int maxn=1010; struct node { int to,next; }e[10010]; int tot; int head[maxn]; void add(int s,int u) { e[tot].to=u; e[tot].next=head[s]; head[s]=tot++; } int st,en; int ti; int dfn[maxn]; int low[maxn]; int num[maxn]; void dfs(int p,int u) { dfn[u]=low[u]=++ti; int i,k; int son=0; for(i=head[u];i;i=e[i].next) { k=e[i].to; if(k!=p&&dfn[k]>0) low[u]=min(low[u],dfn[k]); else if(dfn[k]==0) { son++; dfs(u,k); if(low[k]<low[u]) low[u]=low[k]; if((u==st&&son>=2)||(u!=st&&dfn[u]<=low[k])) num[u]++; } } } int main() { int a,b,i; int ca=1; while(scanf("%d",&a),a) { scanf("%d",&b); ti=0; tot=1; clr(head); st=1005; en=0; st=min(st,a); st=min(st,b); en=max(en,a); en=max(en,b); add(a,b); add(b,a); while(scanf("%d",&a),a) { scanf("%d",&b); st=min(st,a); st=min(st,b); en=max(en,a); en=max(en,b); add(a,b); add(b,a); } clr(dfn); clr(low); clr(num); dfs(0,st); printf("Network #%d\n",ca++); bool flag=true; for(i=st;i<=en;i++) if(num[i]) { flag=false; printf(" SPF node %d leaves %d subnets\n",i,num[i]+1); } if(flag) printf(" No SPF nodes\n"); printf("\n"); } return 0; }