p1127(dfs+欧拉通路/回路)
1.题目分析
我们需要找出一条包含所有单词,这些单词在词链中出现且仅出现一次,且字典序最小的链。
假设我们对每一个单词连一条从首字母指向尾字母的有向边,假设存在这样的一条链,那么我
们所建成的图中便一定存在欧拉通路或者欧拉回路。
欧拉通路 从一点出发,可以经过图中每一条边的路径,被称作欧拉通路,无向图中欧拉
欧拉通路存在的条件:若图中有且仅由两个点的出度不等于入度,且一点的出度=入度+1,另一
点的入度=出度+1,则图中存在欧拉通路,且起点为出度较大的点,终点为入度较大的点。
欧拉回路:从图中任意一点出发,可经过所有边且回到起点的路径
有向图欧拉回路判断条件:所有点的出度等于入读
注意,欧拉回路和通路存在的必要条件是基图联通
首先,为什么我们要建无向图而不是有向图?考虑到词链不可反转(假设词链ab.bc合法,那么c
b.ba不合法),所以只能建有向图
为什么一定要存在欧拉回路或者通路呢?
分析题目,我们将单词转化为边,那么所求的词链一定是一条欧拉通路
有了这个前提,这道题就很容易解决的,
我们将找词链转化为有向图找欧拉通路
但词链的起点怎么确定呢?
假设图中存在欧拉回路,那么从任意点出发都可以,但我们要求字典序最小,所以必须从字典
序最小的单词的首字母出发
假设图中仅有欧拉通路,那么只有从通路起点出发才可经过所有边。
因为我们需要求字典序最小的词链,所以选点按字典序从小开始选即可
分析到这,代码就可以写出来了
建图后先通过并查集来判是否连通,统计出度入度判断图的类型,找到起点按字典序来dfs即可
代码如下
#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cstring>
#include<string>
using namespace std;
const int maxn=1e3+10;
int n;
string st[maxn];
struct node{
int s,t;
}e[maxn];
int cu[maxn];
int ru[maxn];
int fla[maxn];
int cnt;
void add(int x,int y){
cnt++;
e[cnt].s=x;
e[cnt].t=y;
}
int f[maxn];
int find(int x){
if(f[x]!=x){
return f[x]=find(f[x]);
}
else
return x;
}
int tot;
bool jud(){
for(int i=1;i<=26;i++){
f[i]=i;
}
for(int i=1;i<=cnt;i++){
int a=e[i].s;
int b=e[i].t;
int fa = find(a),fb = find(b);
if(fa != fb) f[fa] = fb;
}
int cn=0;
for(int i=1;i<=26;i++){
if(fla[i]&&f[i]==i){
cn++;
}
}
if(cn-1!=tot){
return false;
}
return true;
}
int vis[maxn];
int p;
int ans[maxn];
void dfs(int now){
for(int i=1;i<=n;i++){
if(st[i][0]-'a'+1==now&&!vis[i]){
vis[i]=1;
dfs(st[i][st[i].length()-1]-'a'+1);
p++;
ans[p]=i;
}
}
return ;
}
int main(){
// freopen("a.in","r",stdin);
cin>>n;
for(int i=1;i<=n;i++){
cin>>st[i];
}
int a,b;
sort(st+1,st+1+n);//从小到大排序,目的是在dfs过程中得到最优解
for(int i=1;i<=n;i++){
//cout<<st[i]<<endl;
a=st[i][0]-'a'+1;//得到字符串的起始字符
b=st[i][st[i].length()-1]-'a'+1;//终止字符
// cout<<a<<" "<<b<<endl;
add(a,b);
// add(b,a) ;
if(!fla[a])
fla[a]=1;//记录该字符是否出现,目的是判断联通
if(!fla[b])
fla[b]=1;
cu[a]++;//出度
ru[b]++;//入度
}
// cout<<jud();
if(!jud()){
cout<<"***";
return 0;
}
int s=0;
int t=0;
int to=0;
for(int i=1;i<=26;i++){
if(ru[i]!=cu[i]){
to++;//
if(cu[i]-ru[i]==1){
s=i;
}
if(ru[i]-cu[i]==1){
t=i;
}
}
else if(abs(cu[i]-ru[i])>1){
cout<<"***";
return 0;
}
if(to==2&&(!s||!t)){
cout<<"***";
return 0;
}
}
if(to==1||to>2){
cout<<"***";
return 0;
}
if(s!=0){
dfs(s);
}
else{
dfs(st[1][0]-'a'+1);
}
for(int i=p;i>=1;i--){
cout<<st[ans[i]];
if(i>1)
cout<<'.';
}
return 0;
}