P1127 词链
题目描述
如果单词X的末字母与单词Y的首字母相同,则X与Y可以相连成X.Y。(注意:X、Y之间是英文的句号“.”)。例如,单词dog与单词gopher,则dog与gopher可以相连成dog.gopher。
另外还有一些例子:
dog.gopher
gopher.rat
rat.tiger
aloha.aloha
arachnid.dog
连接成的词可以与其他单词相连,组成更长的词链,例如:
aloha.arachnid.dog.gopher.rat.tiger
注意到,“.”两边的字母一定是相同的。
现在给你一些单词,请你找到字典序最小的词链,使得这些单词在词链中出现且仅出现一次。
输入输出格式
输入格式:
第一行是一个正整数n(1 ≤ n ≤ 1000),代表单词数量。
接下来共有n行,每行是一个由1到20个小写字母组成的单词
输出格式:
只有一行,表示组成字典序最小的词链,若不存在则只输出三个星号“***”。
输入输出样例
6 aloha arachnid dog gopher rat tiger
aloha.arachnid.dog.gopher.rat.tiger
说明
对于40%的数据,有n≤10;
对于100%的数据,有n≤1000。
这一题是典型的欧拉道路题目。 欧拉道路的定义是: 除了起点和终点外, 其他点的“进出” 次数应该相等。 换句话说,除了起点和终点外, 其他点的度数应该是偶数。
对于有向图, 则必须其中一个点的出度恰好比入度大1, 另一个的入度比出度大。
如果奇点数不存在的话, 则可以从任意点出发,最终一定会回到该点(成为欧拉回路)。
题目给的单词量比较大,但是有用的只有首和尾的字母,所以只需要存首尾字母就可以了。
欧拉道路还有关键的一部是判断这一个图是连通的, 并且只有一个一个连通分支。
这个可以用并查集的方法, 也可一用dfs直接搜索。
1.单词作点 可以拼在一起的单词连边。
2.先对单词排序,加边时注意顺序,使找到的第一个欧拉回路即为字典序最小的,后面直接跳出就好了。
3.dfs前初步判断一下是否有解(注意,是初步!!!) 用一些奇怪的方法
4.dfs后,若找到解则输出,否则***。(因为初步判断有解时可能将一些无解的情况放过了)
1 #include <bits/stdc++.h> 2 using namespace std; 3 const int N=1010,inf=1<<29; 4 int n,len[N],sta[N],cnt[30],top=0; 5 int head[N],to[N*N],next1[N*N],en; 6 bool flag=false,vis[N]; 7 string s[N]; 8 void dfs(int u) 9 { 10 if (flag) return ; 11 sta[top++]=u; 12 vis[u]=true; 13 int v; 14 for (int i=head[u];i;i=next1[i]) 15 { 16 v=to[i]; 17 if (!vis[v]) dfs(v); 18 } 19 //如果链上了所有点,说明成功了 20 if (top==n) flag=true; 21 else vis[u]=false,--top; 22 } 23 //日常数组模拟链表,没毛病 24 void add_edge(int u,int v){ 25 to[++en]=v,next1[en]=head[u],head[u]=en; 26 } 27 int main() 28 { 29 //读入数据 30 cin>>n; 31 for (int i=1;i<=n;i++) 32 cin>>s[i]; 33 //排序 34 sort(s+1,s+n+1); 35 //记录每个单词长度 36 for (int i=1;i<=n;i++) 37 len[i]=s[i].size(); 38 //头尾比较法寻找配对单词 39 for (int i=1;i<=n;i++) 40 for (int j=n;j>=1;j--) 41 //i单词的尾和j单词的头相同 42 //并且这两个单词不是同一个 43 if (i!=j && s[i][len[i]-1]==s[j][0]) 44 //给所有配对的单词添加边 45 add_edge(i,j); 46 //统计每个字母的入度和出度情况(单词头尾的字母) 47 //因为欧拉图对节点出入度有要求 48 //欧拉道路的定义是: 除了起点和终点外, 其他点的“进出” 次数应该相等。 换句话说,除了起点和终点外, 其他点的度数应该是偶数。 49 //对于有向图, 则必须其中一个点的出度恰好比入度大1, 另一个的入度比出度大。 50 for (int i=1;i<=n;i++) 51 cnt[s[i][0]-'a']++,cnt[s[i][len[i]-1]-'a']--; 52 int k=0,h; 53 //初步统计是否符合欧拉图,不符合直接输出不可以 54 for (int i=0;i<26;i++){ 55 if (cnt[i]==1) k++,h=i; 56 if (cnt[i]==2) k=2; 57 if (k==2) break; 58 } 59 //说明有两个入口 60 if (k==2) cout<<"*** "; 61 //看看这个点是不是起点,因为只有起点才回没有单词的末尾匹配 62 else if (k==1){ 63 for (int i=1;i<=n;i++) 64 if (s[i][0]-'a'==h) 65 dfs(i); 66 } 67 else { 68 //k==0的情况,说明成环了 69 //取i节点做起始节点看是否成链 70 for (int i=1;i<=n;i++) 71 dfs(i); 72 } 73 if (!flag) { 74 cout<<"*** "; 75 return 0; 76 } 77 cout<<s[sta[0]]; 78 for (int i=1;i<top;i++) 79 cout<<'.'<<s[sta[i]]; 80 cout<<endl; 81 return 0; 82 }
类似题目:
UVa10129