1523 SPF
問題概要
連結な無向グラフで表されるコンピュータネットワークがある。
このネットワークから、1台のコンピュータを取り除いたとき、ネットワークが2つ以上のサブネットワークに分断されるとき、そのようなノードをSPFと呼ぶ。
ネットワークの全てのSPFおよび、そのノードを取り除いたときにできるサブネットワークの数を求めよ。
解法
グラフの関節点を求める問題である。
グラフの関節点は以下のようなアルゴリズムで求められる。
配列num,lowを用意する。
- 一点からdfsし、
- 行きがけに頂点に番号を小さい順に振る
- 帰りがけに、low[v]=min{num[v],num[vから出ている後退辺の行き先],low[vの子]}
を計算する。
low[]はdfsしたときの探索木を、後退辺を一度だけ通って行ける最も小さい番号の頂点である。
そして、根については(探索木において)2個以上の個があったときに関節点、
それ以外の頂点vについては、vの子wで、low[w]≧num[v]を満たすものが存在すれば関節点である。
ここで、low[w]の値の集合のうち、num[v]以上のものの個数m個とすれば、
m+1個が関節点を取り除いたときにできるサブグラフの個数となる。
ソースコード
int n,num[1000],low[1000],na,pa[1000]; pi ans[1000]; vector<vi> e; void rec(int c,int p) { num[c]=n++; pa[c]=p; fr(i,e[c])if(num[*i]<0)rec(*i,c); low[c]=num[c]; fr(i,e[c])if(*i!=p) { if(num[*i]<num[c])low[c]=min(low[c],num[*i]); else low[c]=min(low[c],low[*i]); } set<int> s; if(c!=0) { fr(i,e[c])if(*i!=p&&low[*i]>=num[c])s.insert(low[*i]); if(s.empty())return; ans[na++]=mp(c+1,s.size()+1); } else { int cnt=0; fr(i,e[c])if(pa[*i]==0)cnt++; if(cnt>1)ans[na++]=mp(c+1,cnt); } } int main() { int a,b,cs=0; while(scanf("%d",&a),a) { e.clear(); e.resize(1000); scanf("%d",&b); a--; b--; e[a].pb(b); e[b].pb(a); while(scanf("%d",&a),a) { scanf("%d",&b); a--; b--; e[a].pb(b); e[b].pb(a); } rep(i,1000)num[i]=-1; n=na=0; rec(0,-1); printf("Network #%d\n",++cs); if(na==0)puts(" No SPF nodes"); else { sort(ans,ans+na); rep(i,na)printf(" SPF node %d leaves %d subnets\n",ans[i].first,ans[i].second); } puts(""); } return 0; }