POJ 1386 Play on Words
問題
単語のリストが与えられる。
このリストの単語を使ってしりとりをする。
与えられた単語を、全てちょうど1度ずつ使うしりとりができるかどうかを判定せよ。
制約条件
単語の数≦100000
方針
'a'から'z'までの文字を頂点とするグラフを考える。
各単語について、先頭の文字の頂点から終端の文字の頂点まで、有向の枝を張る。
すると、単語をちょうど1度ずつ使うしりとりというのは、このグラフ上におけるオイラー歩(全ての辺を一度ずつ通るようなパス)に相当する。
有向グラフがオイラー歩をもつかは、グラフが連結かつ各頂点の相対次数(入次数-出次数)が、
- +1がひとつ、-1がひとつ
- すべて0
のどちらかが成り立つことが必要十分である。
(必要性は明らかで、逆も無向オイラー歩の条件の証明と同じように考えればわかる)
ソースコード
int n; char str[1001]; int g[26][26], deg[26]; bool v[26]; void dfs(int c){ v[c]=1; rep(i,26)if(g[c][i]&&!v[i])dfs(i); } int main(){ int T; scanf("%d",&T); while(T--){ scanf("%d",&n); rep(i,26)v[i]=1, deg[i]=0; rep(i,n){ scanf("%s",str); char a=str[0], b=str[strlen(str)-1]; v[a-'a']=v[b-'a']=0; g[a-'a'][b-'a']=1; deg[a-'a']++; deg[b-'a']--; } int s=-1, t=-1; rep(i,26){ if(deg[i]==1){ if(s==-1)s=i; else s=-2; } else if(deg[i]==-1){ if(t==-1)t=i; else t=-2; } else if(deg[i]!=0){ s=t=-2; } } if(s==-2)goto FAIL; if(s==-1){ rep(i,26)if(!v[i])s=i; } dfs(s); rep(i,26)if(!v[i])goto FAIL; puts("Ordering is possible."); continue; FAIL:puts("The door cannot be opened."); } return 0; }