题意:给你一些单词,请你判断能否把他们首尾串起来串成一串,前一个单词的结尾字母应该与下一个单词的首字母相同。并且输出所有方案中字典序最小的方案。
分析:这题实际上是求解欧拉回路,先根据欧拉回路的性质判断存不存在拼接方案,然后判断是欧拉回路还是欧拉通路,若是通路则只存在一个起点,则以那个点为起点进行dfs保存结果后输出,若是欧拉回路,则找到最小的字母为起点,然后进行dfs同样保存结果并输出。
有向图欧拉回路的判断:若一个图是连通的,并且所有顶点的入度等于出度,则存在欧拉回路,可以以任意点为起点再回到该点。
有向图的欧拉通路的判定:若一个图是连通的,并且除两个点外所有点的入度等于出度,而且那两个点,一个出度与入度之差为1,另一个出入度之差为-1,则存在欧拉通路。该通路一定是以出入之差为1的点为起点,然后回到出入度之差为-1的点。
代码:
#include <iostream>
#include <algorithm>
#include <cstring>
#include <cstdlib>
#include <cstdio>
#include <vector>
#include <string>
using namespace std;
const int maxn = 35;
const int maxm = 1005;
struct UFset{
int p[maxn];
UFset(){}
int Find(int x){return p[x] >= 0 ? p[x] = Find(p[x]) : x;}
void Union(int r1, int r2){
r1 = Find(r1);
r2 = Find(r2);
int t = p[r1] + p[r2];
if(p[r1] < p[r2]){
p[r2] = r1;
p[r1] = t;
}
else{
p[r1] = r2;
p[r2] = t;
}
}
void Clear(){
for(int i = 0; i < maxn; i++)
p[i] = -1;
}
};
struct node{
int v;
int No;
string word;
node(int _v, int _No, string _word){
v = _v; No = _No; word = _word;
}
bool operator < (const node &b) const{
return word < b.word;
}
};
int in[maxn];//入度
int out[maxn];//出度
int m;
char word[maxm][35];
vector<node> list[maxn];
bool flag[maxn];//'a'~'z'这些结点是否出现
bool vis[maxm];//for dfs
UFset Set;
int ans[maxm];//结果路径
int s;//出入度之差为1的点
int t;//出入度之差为-1的点
bool ok;//dfs是否找到了结果
void input(){
scanf("%d",&m);
Set.Clear();
for(int i = 0; i < maxn; i++) {
list[i].clear(); flag[i] = false;
in[i] = 0; out[i] = 0;
}
for(int i = 0; i < m; i++){
scanf("%s",word[i]);
int u = word[i][0] - 'a';
int v = word[i][strlen(word[i])-1] - 'a';
flag[u] = true; flag[v] = true;
out[u]++; in[v]++;
if(Set.Find(u) != Set.Find(v))
Set.Union(u,v);
list[u].push_back(node(v,i,word[i]));
vis[i] = false;//
}
//确保结果是字典序最小
for(int i = 0; i < maxn; i++){
sort(list[i].begin(),list[i].end());
}
}
bool is_euler(){
//判断是否是联通图
int first = -1;
for(int i = 0; i < maxn; i++){
if(!flag[i]) continue;
if(first == -1) first = i;
if(Set.Find(first) != Set.Find(i))
return false;
}
s = -1;
t = -1;
for(int i = 0; i < maxn; i++){
if(!flag[i]) continue;
if(in[i] == out[i]) continue;
if(abs(out[i] - in[i]) > 1) return false;
if(out[i]-in[i] == 1){
if(s == -1) s = i;
else return false;
}
if(out[i]-in[i] == -1){
if(t == -1) t = i;
else return false;
}
}
return true;
}
void dfs(int s, int d){
if(d == m){
ok = true; return;
}
for(int i = 0; i < list[s].size(); i++){
int v = list[s][i].v;
int No = list[s][i].No;
if(!vis[No]){
vis[No] = true;
ans[d] = No;
dfs(v,d+1);
vis[No] = false;
}
if(ok) return;
}
}
void solve(){
//如果是欧拉回路,则取字典序最小的点出发
if(s == -1){
for(int i = 0; i < maxn; i++){
if(!flag[i]) continue;
s = i; break;
}
}
ok = false;
dfs(s,0);
printf("%s",word[ans[0]]);
for(int i = 1; i < m; i++){
printf(".%s",word[ans[i]]);
}
puts("");
}
int main(){
int t;
scanf("%d",&t);
while(t--){
input();
if(is_euler()) solve();
else puts("***");
}
return 0;
}