微信扫一扫打赏支持

P1127 词链

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个小写字母组成的单词

 

输出格式:

 

只有一行,表示组成字典序最小的词链,若不存在则只输出三个星号“***”。

 

输入输出样例

输入样例#1:
6
aloha
arachnid
dog
gopher
rat
tiger
输出样例#1:
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<<"***\n";
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<<"***\n";
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

 

posted @ 2017-08-26 04:16  范仁义  阅读(961)  评论(0编辑  收藏  举报