题意:给定一些单词,如果一个单词的尾字母与另一个的首字母相同则可以连接。问是否可以每个单词用一次,将所有单词连接,可以则输出字典序最小的序列。
分析:把每个字母作为一个结点。每个单词作为一条边。这样就可以把问题转化为欧拉路径。现判断欧拉路径是否存在。若存在则找到欧拉路径,找的方法是,先找到出度>入度(无欧拉回路)的结点,从起点开始,dfs(v,e),v是当前结点,e是到v的边。用vis数组记录某条边是否被访问过,从v开始继续搜索未访问过的边。每层dfs结束时把该层的边入栈。dfs整个结束后,把边依次出栈,得到的就是欧拉路径。
void dfs(int v, int e)
{
vis[e] = true;
for (int i = head[v]; i != -1; i = edge[i].next)
if (!vis[i])
dfs(edge[i].v, i);
stk[top++] = e;
}
本题要求字典序最小,那么在dfs的时候就每次先访问字典序最小的边(原因很难说清),具体做法就是把边插入邻接表的时候冒泡一下,维护邻接表有序。当有欧拉回路时还要注意起点的选择,要使得路径的第一条边最小。
#include <iostream>
#include <cstdio>
#include <cstdlib>
#include <cstring>
using namespace std;
#define maxn 30
#define maxl 25
#define maxm 1005
struct Edge
{
int v, next, id;
} edge[maxm];
int n, m;
char word[maxm][maxl];
int father[maxn];
int stk[maxm];
int in[maxn], out[maxn];
int head[maxn];
int ecount;
int top, s;
bool vis[maxm];
bool rep;
int getanc(int a)
{
if (a == father[a])
return a;
return father[a] = getanc(father[a]);
}
void merge(int a, int b)
{
father[getanc(a)] = getanc(b);
}
void addedge(int a, int b, int c)
{
edge[ecount].next = head[a];
edge[ecount].id = c;
edge[ecount].v = b;
head[a] = ecount++;
int temp = head[a];
while (edge[temp].next != -1)
{
int u = edge[temp].id;
int v = edge[edge[temp].next].id;
if (strcmp(word[u], word[v]) > 0)
{
swap(edge[temp].v, edge[edge[temp].next].v);
swap(edge[temp].id, edge[edge[temp].next].id);
}
temp = edge[temp].next;
}
}
void input()
{
for (int i = 0; i < n; i++)
father[i] = i;
memset(in, 0, sizeof(in));
memset(out, 0, sizeof(out));
ecount = 0;
memset(head, -1, sizeof(head));
scanf("%d", &m);
for (int i = 0; i < m; i++)
{
scanf("%s", word[i]);
int a = word[i][0] - 'a';
int b = word[i][strlen(word[i]) - 1] - 'a';
addedge(a, b, i);
merge(a, b);
in[b]++;
out[a]++;
}
}
bool ok()
{
s = 0;
int cnt1 = 0, cnt2 = 0;
for (int i = 0; i < n; i++)
{
if (abs(in[i] - out[i]) > 1)
return false;
if (in[i] - out[i] == 1)
cnt1++;
if (out[i] - in[i] == 1)
{
s = i;
cnt2++;
}
}
if (cnt1 == 0 && cnt2 == 0)
rep = true;
else
rep = false;
if (cnt1 > 1 || cnt2 > 1 || cnt1 != cnt2)
return false;
cnt1 = 0;
for (int i = 0; i < n; i++)
if (i == father[i] && (in[i] | out[i]))
cnt1++;
if (cnt1 > 1)
return false;
bool first = true;
if (rep)
for (int i = 0; i < n; i++)
if (head[i] != -1 && (first || strcmp(word[edge[head[s]].id], word[edge[head[i]].id]) > 0))
{
first = false;
s = i;
}
return true;
}
void dfs(int v, int e)
{
vis[e] = true;
for (int i = head[v]; i != -1; i = edge[i].next)
if (!vis[i])
dfs(edge[i].v, i);
stk[top++] = e;
}
void work()
{
memset(vis, 0, sizeof(vis));
top = 0;
dfs(s, ecount);
}
void print()
{
top--;
printf("%s", word[edge[stk[top - 1]].id]);
top--;
while (top > 0)
{
printf(".%s", word[edge[stk[top - 1]].id]);
top--;
}
putchar('\n');
}
int main()
{
//freopen("t.txt", "r", stdin);
int t;
scanf("%d", &t);
while (t--)
{
n = 26;
input();
if (!ok())
{
printf("***\n");
continue;
}
else
work();
print();
}
return 0;
}