POJ2337 欧拉回路
题意:给你n个由小写字母组成的单词,要求将这n个单词连接起来,使得前一个单词的最后一个字母和后一个单词的第一个字母相容,输出字典序最小的解
思路:不难发现此题可以转化为欧拉路径问题,把每个字符串的第一个字母当作起点,最后一个字母当作终点,连一条有向边,求此图字典序最小的欧拉路径。
对于有向图来说,存在一条欧拉路径的充要条件为此图联通并且要么对于每个顶点都有出度等于入度,要么有且仅有一个顶点出度比入度少1,有且仅有一个顶点入度比出度少1,其余顶点出度等于入度。
对于联通性的判断来说,直接用dfs即可,只要能把所有的边都遍历到,此图就是联通的。当然也可以用并查集判断联通。
对于字典序最小的要求,只需把给定的所有字符串排序,按照字典序从小到大的顺序加边即可。
代码如下
#include<iostream>
#include<algorithm>
#include<string>
#include<cstring>
#include<vector>
using namespace std;
string s[1005];
int in[26],out[26];
struct node
{
int y,id,flag;
node(int ty,int tid,int tflag):y(ty),id(tid),flag(tflag){}
node(){}
};
vector<node> g[26];
int ans[1005];
int cnt=0;
void dfs(int now)
{
for(int i=0;i<g[now].size();i++)
{
if(!g[now][i].flag)
{
g[now][i].flag=1;
dfs(g[now][i].y);
ans[++cnt]=g[now][i].id;
}
}
}
int main()
{
ios_base::sync_with_stdio(0);
int T;
cin>>T;
while(T--)
{
for(int i=0;i<26;i++)
g[i].clear();
memset(in,0,sizeof in);
memset(out,0,sizeof out);
int n;
cin>>n;
for(int i=1;i<=n;i++)
cin>>s[i];
sort(s+1,s+1+n);
int st=100;
for(int i=1;i<=n;i++)
{
int a=s[i][0]-'a';
int b=s[i][s[i].size()-1]-'a';
st=min(st,a);
g[a].push_back(node(b,i,0));
out[a]++;in[b]++;
}
int d1=0,d2=0;
for(int i=0;i<26;i++)
{
if(out[i]-in[i]==1)
{
d1++;
st=i;
}
else if(in[i]-out[i]==1)
{
d2++;
}
else if(out[i]-in[i])
{
d1=100;
}
}
if(!(d1==1&&d2==1||d1==0&&d2==0))
{
printf("***\n");
continue;
}
cnt=0;
dfs(st);
if(cnt!=n)
{
printf("***\n");
continue;
}
for(int i=cnt;i>=1;i--)
{
cout<<s[ans[i]];
if(i==1) cout<<endl;
else cout<<".";
}
}
return 0;
}