题目大意:给出一系列单词,当某个单词的首字母和前一个单词的尾字母相同,则这两个单词能链接起来。给出一系列单词,问是否能够连起来。
题目分析:以单词的首尾字母为点,单词为边建立有向图,便是判断图中是否存在欧拉道路。有向图中存在欧拉路径的两个条件是:1、忽略边的方向性后,底图联通;2、奇点个数为0时、奇点个数为2并且满足起点的入度比出度小1和终点的出度比入度大1时,欧拉道路一定存在;
判断图的连通性有两种方法:1、利用并查集,只判断有几个根节点即可;2、使用DFS,做法实质上就是判断联通块的个数;
利用并查集:
# include<iostream> # include<cstdio> # include<map> # include<set> # include<string> # include<cstring> # include<algorithm> using namespace std; int n,in[26],out[26],fa[26],mark[26]; char p[1005]; int fin(int u) { int x=u; while(fa[u]!=u) u=fa[u]; while(fa[x]!=u){ int k=fa[x]; fa[x]=u; x=k; } return u; } int get() { int cnt=0; for(int i=0;i<26;++i) if(mark[i]&&fa[i]==i) ++cnt; return cnt; } bool judge() { if(get()>1) return false; int cnt=0; for(int i=0;i<26;++i) if(mark[i]&&in[i]!=out[i]) ++cnt; if(cnt>2) return false; if(cnt==0) return true; if(cnt==1) return false; int k1=0,k2=0; for(int i=0;i<26;++i){ if(mark[i]&&in[i]!=out[i]){ if(in[i]+1==out[i]) k1=1; if(in[i]==out[i]+1) k2=1; } } return k1&&k2; } int main() { int T; scanf("%d",&T); while(T--) { memset(in,0,sizeof(in)); memset(out,0,sizeof(out)); memset(mark,0,sizeof(mark)); for(int i=0;i<26;++i) fa[i]=i; scanf("%d",&n); for(int i=0;i<n;++i){ scanf("%s",p); int l=strlen(p); mark[p[0]-'a']=mark[p[l-1]-'a']=1; ++out[p[0]-'a']; ++in[p[l-1]-'a']; int u=fin(p[0]-'a'); int v=fin(p[l-1]-'a'); if(u!=v) fa[u]=v; } if(judge()) printf("Ordering is possible. "); else printf("The door cannot be opened. "); } return 0; }
使用DFS:
# include<iostream> # include<cstdio> # include<map> # include<set> # include<string> # include<cstring> # include<algorithm> using namespace std; int n,in[26],out[26],mark[26],vis[26],mp[26][26]; char p[1005]; void dfs(int u) { for(int i=0;i<26;++i){ if(mark[i]&&!vis[i]&&mp[u][i]){ vis[i]=1; dfs(i); } } } bool judge() { int cnt=0; memset(vis,0,sizeof(vis)); for(int i=0;i<26;++i){ if(mark[i]&&!vis[i]){ ++cnt; vis[i]=1; dfs(i); } } if(cnt>1) return false; for(int i=0;i<26;++i) if(mark[i]&&vis[i]==0) return false; cnt=0; for(int i=0;i<26;++i) if(mark[i]&&in[i]!=out[i]) ++cnt; if(cnt>2) return false; if(cnt==0) return true; if(cnt==1) return false; int k1=0,k2=0; for(int i=0;i<26;++i){ if(mark[i]&&in[i]!=out[i]){ if(in[i]+1==out[i]) k1=1; if(in[i]==out[i]+1) k2=1; } } return k1&&k2; } int main() { int T; scanf("%d",&T); while(T--) { memset(in,0,sizeof(in)); memset(mp,0,sizeof(mp)); memset(out,0,sizeof(out)); memset(mark,0,sizeof(mark)); scanf("%d",&n); for(int i=0;i<n;++i){ scanf("%s",p); int l=strlen(p); mp[p[0]-'a'][p[l-1]-'a']=mark[p[0]-'a']=mark[p[l-1]-'a']=1; ++out[p[0]-'a']; ++in[p[l-1]-'a']; } if(judge()) printf("Ordering is possible. "); else printf("The door cannot be opened. "); } return 0; }