洛谷P1127 【词链】欧拉通路,欧拉回路+dfs

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;
}
posted @ 2020-10-09 00:39  折翼的小鸟先生  阅读(241)  评论(0编辑  收藏  举报