117 C Cycle
問題
トーナメントであるグラフとは、どの二つの頂点u,vも、u,vの間にちょうど一本の辺(u,v)または(v,u)があるようなグラフを言う。
n頂点からなるトーナメントなグラフが与えられるとき、このグラフが長さ3の閉路をもつならば、その3つの頂点を出力し、そうでなければ-1を出力せよ。
制約条件
n≦5000
方針
少し考えると、トーナメントなグラフが長さ4以上の閉路を持つとき、そのグラフは長さ3の閉路を必ず持つことがわかる。
例えば、
v1→v2→v3→v4→v5→v6→v1みたいな閉路があった場合、
v1とv3には辺があるはずで、v1→v3の向きだったらv4→v5→v6→v1が閉路に、
v3→v1の向きだったらv1→v2→v3が閉路になる。
これを長い閉路にどんどん適用していくと長さ3の閉路が得られる。
ということで、ループを検出して、
v1→v2→v3→v4→v5→v6→v1みたいなループがあった場合、
- (v1,v2)を含む長さ3の閉路
- (v2,v3)を含む長さ3の閉路
- (v3,v4)を含む長さ3の閉路
- (v4,v5)を含む長さ3の閉路
- (v5,v6)を含む長さ3の閉路
- (v6,v1)を含む長さ3の閉路
のいずれかがある。
のでそれを全部見てやる。
ループの検出にはDFSを一度やればいいのでO(n).
ループが一度検出されたら、ループの、
それぞれの辺について、それを含むような長さ3の閉路があるかを調べる。これにO(n^2)かかる。
ので、全体の計算量はO(n^2)となる。
ソースコード
int n,visit[5000],prev[5000]; bool e[5000][5000],found; void rec(int c){ visit[c]=1; if(found)return; rep(i,n)if(e[c][i]){ if(visit[i]==1){ found=1; vi v; v.pb(i); for(;c!=i;c=prev[c])v.pb(c); v.pb(i); rep(j,v.size()-1)rep(k,n)if(e[v[j]][k]&&e[k][v[j+1]]){ cout<<v[j]+1<<" "<<k+1<<" "<<v[j+1]+1<<endl; return; } } else if(visit[i]==0){ prev[i]=c; rec(i); if(found)return; } } visit[c]=2; } void run(){ scanf("%d",&n); rep(i,n){ char in[5001]; scanf("%s",in); rep(j,n)e[i][j]=in[j]=='1'; visit[i]=0; prev[i]=-1; } found=0; rep(i,n)if(!visit[i]){ rec(i); if(found)return; } cout<<-1<<endl; }