洛谷P1127 【詞鏈】歐拉通路,歐拉回路+dfs

p1127(dfs+歐拉通路/迴路)


題目連接node

1.題目分析

咱們須要找出一條包含全部單詞,這些單詞在詞鏈中出現且僅出現一次,且字典序最小的鏈。ios

假設咱們對每個單詞連一條從首字母指向尾字母的有向邊,假設存在這樣的一條鏈,那麼我c++

們所建成的圖中便必定存在歐拉通路或者歐拉回路。spa

歐拉通路 從一點出發,能夠通過圖中每一條邊的路徑,被稱做歐拉通路,無向圖中歐拉code

歐拉通路存在的條件:若圖中有且僅由兩個點的出度不等於入度,且一點的出度=入度+1,另外一排序

點的入度=出度+1,則圖中存在歐拉通路,且起點爲出度較大的點,終點爲入度較大的點。ci

歐拉回路:從圖中任意一點出發,可通過全部邊且回到起點的路徑字符串

有向圖歐拉回路判斷條件:全部點的出度等於入讀get

注意,歐拉回路和通路存在的必要條件是基圖聯通string

首先,爲何咱們要建無向圖而不是有向圖?考慮到詞鏈不可反轉(假設詞鏈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;
}
相關文章
相關標籤/搜索