
本文共 4262 字,大约阅读时间需要 14 分钟。
文章目录
欧拉路径/并查集 - Play on Words - POJ 1386
题意:
有 N 个盘子,每个盘子上写着一个仅由小写字母组成的英文单词。
你需要给这些盘子安排一个合适的顺序,使得相邻两个盘子中,前一个盘子上单词的末字母等于后一个盘子上单词的首字母。
请你编写一个程序,判断是否能达到这一要求。
输入格式
第一行包含整数 T,表示共有 T 组测试数据。
每组数据第一行包含整数 N,表示盘子数量。
接下来 N 行,每行包含一个小写字母字符串,表示一个盘子上的单词。
一个单词可能出现多次。
输出格式
如果存在合法解,则输出”Ordering is possible.”,否则输出”The door cannot be opened.”。
数据范围
1 ≤ N ≤ 1 0 5 , 单 词 长 度 均 不 超 过 1000 1≤N≤10^5, 单词长度均不超过1000 1≤N≤105,单词长度均不超过1000
输入样例:
32acmibm3acmmalformmouse2okok
输出样例:
The door cannot be opened.Ordering is possible.The door cannot be opened.
分析:
建图:
建 图 方 式 是 重 中 之 重 , 由 于 单 词 的 数 量 在 1 0 5 数 量 级 , 建图方式是重中之重,由于单词的数量在10^5数量级, 建图方式是重中之重,由于单词的数量在105数量级,
考 虑 到 单 词 连 接 的 方 式 是 首 尾 相 连 , 故 我 们 可 以 将 每 个 单 词 看 作 一 条 边 , 边 的 端 点 为 首 部 字 母 和 末 尾 字 母 。 考虑到单词连接的方式是首尾相连,故我们可以将每个单词看作一条边,边的端点为首部字母和末尾字母。 考虑到单词连接的方式是首尾相连,故我们可以将每个单词看作一条边,边的端点为首部字母和末尾字母。
问题就转化为给定m条有向边,判断是否存在欧拉路径。
有向图的欧拉路径:
① 、 所 有 点 的 入 度 与 出 度 均 相 等 。 或 者 , ①、所有点的入度与出度均相等。或者, ①、所有点的入度与出度均相等。或者,
② 、 起 点 的 出 度 比 入 度 大 1 , 终 点 的 入 度 比 出 度 大 1 , 其 他 点 的 入 度 与 出 度 相 等 。 ②、起点的出度比入度大1,终点的入度比出度大1,其他点的入度与出度相等。 ②、起点的出度比入度大1,终点的入度比出度大1,其他点的入度与出度相等。
本题还需注意连通性问题:
判 断 是 否 存 在 孤 立 点 : 判断是否存在孤立点: 判断是否存在孤立点:
① 、 并 查 集 : 若 存 在 两 个 点 的 祖 宗 节 点 不 同 , 说 明 存 在 孤 立 点 。 ①、并查集:若存在两个点的祖宗节点不同,说明存在孤立点。 ①、并查集:若存在两个点的祖宗节点不同,说明存在孤立点。
② 、 欧 拉 路 径 : d f s 跑 一 遍 欧 拉 路 径 , 若 经 过 的 边 的 数 量 小 于 m , 说 明 存 在 孤 立 点 。 ②、欧拉路径:dfs跑一遍欧拉路径,若经过的边的数量小于m,说明存在孤立点。 ②、欧拉路径:dfs跑一遍欧拉路径,若经过的边的数量小于m,说明存在孤立点。
方法一:欧拉路径:
#include<iostream>#include<cstring>#include<cstdio>#include<algorithm>using namespace std;const int N=30, M=100010;int T,n=26,m,cnt;int din[N],dout[N];int g[N][N];char str[1010];bool has_euler() //判断充要条件{ int start=0,end=0; for(int i=0;i<n;i++) if(din[i]!=dout[i]) { if(din[i]+1==dout[i]) start++; else if(dout[i]+1==din[i]) end++; else return false; } if(!(!start && !end || start==1 && end==1)) return false; //同时确保了奇数度的点数量为偶数 return true;}void dfs(int u){ for(int i=0;i<n;i++) if(g[u][i]) { cnt++; g[u][i]--; dfs(i); }}int main(){ scanf("%d",&T); while(T--) { cnt=0; memset(g,0,sizeof g); memset(din,0,sizeof din); memset(dout,0,sizeof dout); scanf("%d",&m); for(int i=0;i<m;i++) { scanf("%s",str); int len=strlen(str); int a=str[0]-'a', b=str[len-1]-'a'; g[a][b]++; din[b]++, dout[a]++; } int S=0; while(!dout[S]) S++; for(int i=0;i<n;i++) if(din[i]+1==dout[i]) { S=i; break; } if(has_euler()) dfs(S); if(cnt<m) puts("The door cannot be opened."); else puts("Ordering is possible."); } return 0;}
方法二:并查集:
#include<iostream>#include<cstring>#include<cstdio>#include<algorithm>using namespace std;const int N=30, M=100010;int T,n=26,m;int p[N];int din[N],dout[N];bool st[N];char str[1010];int Find(int x){ if(p[x]!=x) p[x]=Find(p[x]); return p[x];}bool is_connected() //连通性判断{ int root=-1; for(int i=0;i<n;i++) if(st[i]) { if(root==-1) root=Find(i); if(root!=Find(i)) return false; } return true;}bool has_euler() //判断充要条件{ int start=0,end=0; for(int i=0;i<n;i++) if(din[i]!=dout[i]) { if(din[i]+1==dout[i]) start++; else if(dout[i]+1==din[i]) end++; else return false; } if(!(!start && !end || start==1 && end==1)) return false; //同时确保了奇数度的点数量为偶数 return true;}int main(){ scanf("%d",&T); while(T--) { for(int i=0;i<n;i++) p[i]=i; memset(st,false,sizeof st); memset(din,0,sizeof din); memset(dout,0,sizeof dout); scanf("%d",&m); for(int i=0;i<m;i++) { scanf("%s",str); int len=strlen(str); int a=str[0]-'a', b=str[len-1]-'a'; din[b]++, dout[a]++; st[b]=st[a]=true; p[Find(a)]=Find(b); } if(is_connected() && has_euler()) puts("Ordering is possible."); else puts("The door cannot be opened."); } return 0;}
发表评论
最新留言
关于作者
