题目大意:
判断单词能否首尾相连构成一个串,如果可以输出字典序最小的方案
和POJ1386一样,只不过这道题需要输出欧拉路径,并且是最小字典序(怎么判断就不说了,详细讲解请见POJ1386)
先忽略字典序最小这个问题……..
我们求欧拉路径用的当然是dfs,当我们dfs的时候发现有一个点无路可走了,那么我们回溯,把来时的边压入栈中,然后重复这个过程,最后我们遍历完所有边之后,把栈中的边依次弹出,得到的就是欧拉路径,至于正确性,你自己手动模拟一遍就理解了
然后考虑怎么让字典序最小
把这些单词(也就是这些边)按照字典序从大到小排序,字典序大的先建边,这样就保证了我们先访问到的一定是字典序较大的边,这条边也就先被压入栈中,后被弹出,所以得到的一定是字典序最小滴……..
以后一定记得好好检查数组下标问题……..今天因为吧rank[i]写成了i,WA了一下午QAQ好忧伤……………..
代码如下:
代码略长…………….
#include<algorithm>
#include<iostream>
#include<cstring>
#include<cstdio>
#include<cmath>
#define Min(a,b,c) min(a,min(b,c))
using namespace std;
const int maxn=1000+5;
char s[maxn][maxn];
int rank[maxn],cas,n,hd[30+5],cnt,in[30+5],out[30+5],S,stk[maxn],tail;
bool vis[maxn];
struct edge{
int to,nxt,w,flag;
}e[maxn*2];
bool cmp(int a,int b){
return max(0,strcmp(s[a],s[b]));
}
void add(int x,int y,int z){
e[cnt].to=y;
e[cnt].w=z;
e[cnt].nxt=hd[x];
e[cnt].flag=false;
hd[x]=cnt++;
in[y]++;
out[x]++;
}
void dfs(int root){
for(int i=hd[root];i!=-1;i=e[i].nxt)
if(!e[i].flag){
e[i].flag=true;
dfs(e[i].to);
stk[++tail]=e[i].w;
}
}
bool checkdfs(){
for(int i=0;i<cnt;i++)
if(!e[i].flag)
return false;
return true;
}
bool check(){
int la=0,lala=0,lalala=0,k;
for(int i=1;i<=26;i++){
if(out[i]-in[i]==1)
lala++,S=i;
if(in[i]-out[i]==1)
la++;
if(abs(out[i]-in[i])>1)
lalala++;
}
if(la>1||lala>1||lalala>0)
return false;
return true;
}
signed main(void){
// freopen("lala.in","r",stdin);
// freopen("lala.out","w",stdout);
scanf("%d",&cas);
while(cas--){
cnt=tail=0,S=1000;
scanf("%d",&n);
memset(hd,-1,sizeof(hd));
memset(in,0,sizeof(in));
memset(out,0,sizeof(out));
memset(vis,false,sizeof(vis));
for(int i=1;i<=n;i++)
scanf("%s",s[i]),rank[i]=i;
sort(rank+1,rank+n+1,cmp);
for(int i=1,x,y,z;i<=n;i++)
x=s[rank[i]][0]-'a'+1,y=s[rank[i]][strlen(s[rank[i]])-1]-'a'+1,add(x,y,rank[i]),S=Min(S,x,y);
if(!check())
cout<<"***"<<endl;
else{
dfs(S);
if(!checkdfs())
cout<<"***"<<endl;
else{
for(int i=tail;i>1;i--)
cout<<s[stk[i]]<<".";
cout<<s[stk[1]]<<endl;
}
}
}
return 0;
}
by >o< neighthorn