http://poj.org/problem?id=2337
题目大意:给出一组单词,如果两个单词,一个单词的头和另一个单词的尾相同,则可以相连,例如abce, efdg,可以相连,问这组单词能否排成一排,如果可以,求出字典序最小的那个。
思路:重点在建图。将单词的两个端点为顶点建有向边,用邻接表存储,用头插法建邻接表时,首先应对输入的字符串从大到小排序,这样才能保证按字典序输出。
首先判断图的连通性,然后判断是否欧拉路或欧拉通路。若是欧拉路,以出度大入度为1的点为起点,若是欧拉回路则以字典序最小的点为起点进行dfs。
#include <stdio.h>
#include <string.h>
#include <algorithm>
#include <stack>
#include <vector>
#define LL long long
#define _LL __int64
using namespace std;
struct node
{
int u,v;
int next;
char str[22];
int vis;
bool operator < (const struct node &tmp)const
{
if(strcmp(str,tmp.str) > 0)
return true;
return false;
}
}s[1010];
int in[1010],out[1010]; //保存每个点的出入度
int cnt;//顶点个数
int ok[26];//ok[i] = 1 : i+'a'是顶点
int set[26];
int start,end;//记录欧拉路的起点与终点
int head[26];
char ans[1010][22];//保存输出结果
int c;
void init()
{
memset(in,0,sizeof(in));
memset(out,0,sizeof(out));
memset(ok,0,sizeof(ok));
cnt = 0;
for(int i = 0; i < 26; i++)
set[i] = i;
memset(head,-1,sizeof(head));
c = 0;
}
int find(int x)
{
if(set[x] != x)
set[x] = find(set[x]);
return set[x];
}
//并查集判断是否图是否连通
bool check()
{
int flag = 1;
for(int i = 0; i < 26; i++)
{
if(!ok[i]) continue;
for(int j = i+1; j < 26; j++)
{
if(!ok[j]) continue;
if(find(i) != find(j))
{
flag = 0;
break;
}
}
if(!flag) break;
}
if(flag) return true;
return false;
}
//判断是欧拉路还是欧拉回路
int judge()
{
int c1 = 0,c2 = 0,c3 = 0;
for(int i = 0; i < 26; i++)
{
if(ok[i])
{
if(in[i] == out[i])
c3++;
else if(in[i]-out[i] == 1)
{
end = i;
c1++;
}
else if(out[i]-in[i] == 1)
{
start = i;
c2++;
}
}
}
if(c3 == cnt)
return 2; //欧拉回路
else if(c1 == 1 && c2 == 1 && c3 == cnt-2) //欧拉路
return 1;
else
return 0;
}
//dfs寻找欧拉路或欧拉回路,注意输出是逆序输出
void dfs(int u)
{
for(int i = head[u]; i != -1; i = s[i].next)
{
if(!s[i].vis)
{
s[i].vis = 1;
dfs(s[i].v);
strcpy(ans[c++],s[i].str);
}
}
}
int main()
{
int test,n,u,v;
scanf("%d",&test);
while(test--)
{
init();
scanf("%d",&n);
for(int i = 0; i < n; i++)
{
scanf("%s",s[i].str);
s[i].vis = 0;
}
sort(s,s+n); //对边从大到小排序
for(int i = 0; i < n; i++)
{
int len = strlen(s[i].str);
u = s[i].str[0]-'a';
v = s[i].str[len-1]-'a';
if(!ok[u])
{
ok[u] = 1;
cnt++;
}
if(!ok[v])
{
ok[v] = 1;
cnt++;
}
//头插法建边
s[i].u = u;
s[i].v = v;
s[i].next = head[u];
head[u] = i;
out[u]++;
in[v]++;
int uu = find(u);
int vv = find(v);
if(uu != vv)
set[uu] = vv;
}
if(!check())
{
printf("***\n");
continue;
}
int res = judge();
if(!res)
{
printf("***\n");
continue;
}
else if(res == 1) //欧拉路
{
dfs(start);
}
else //欧拉回路
{
for(int i = 0; i < 26; i++)
{
if(ok[i])
{
start = i;
break;
}
}
dfs(start);
}
printf("%s",ans[c-1]);
for(int i = c-2; i >= 0; i--)
printf(".%s",ans[i]);
printf("\n");
}
return 0;
}