POJ 2337 尤拉圖的巧妙運用
阿新 • • 發佈:2018-12-05
題意:
給你一組N個單詞,現在要你輸出這樣一組單詞序列。該序列包含了所有N個單詞,且該序列中的前一個單詞的最後一個字母與後一個單詞的第一個字母相同。如果存在多個這種首尾相連的序列,就輸出字典序最小的那個即可。
程式碼:
#include<cstdio> #include<cstring> #include<algorithm> using namespace std; const int maxn=1000+10; struct Word { int len; char s[20+5]; bool operator <(const Word &b)const { return strcmp(s,b.s)<0; } }words[maxn]; struct Edge { int u,v;//u表示起點,v表示終點 bool vis; }edges[maxn]; int fa[26+5]; //並查集 int in[26+5],out[26+5];//入度,出度 bool mark[26+5]; //mark[i]=true,表i這個字母被用到作為圖頂點了 int m;//單詞數,即邊數 int cnt,ans[maxn];//記錄輸出單詞的序號,逆序 int findset(int u) { if(fa[u]==-1) return u; return fa[u]=findset(fa[u]); } bool ok()//判斷是否弱連通圖 { int k=0; for(int i=0;i<26;i++)if(mark[i]) if(findset(i)==i) k++; return k==1; } void euler(int u) { for(int i=0;i<m;i++)if(!edges[i].vis && edges[i].u==u)//本邊未走過且起點吻合 { edges[i].vis=true; euler(edges[i].v); ans[cnt++]=i; } } int main() { int T; scanf("%d",&T); while(T--) { scanf("%d",&m); for(int i=0;i<m;i++) { scanf("%s",words[i].s); words[i].len=strlen(words[i].s); } sort(words,words+m); memset(fa,-1,sizeof(fa)); memset(mark,0,sizeof(mark)); memset(in,0,sizeof(in)); memset(out,0,sizeof(out)); for(int i=0;i<m;i++) { int u,v; u=words[i].s[0]-'a'; v=words[i].s[words[i].len-1]-'a'; edges[i].u=u; edges[i].v=v; edges[i].vis=false; //表示該邊還未被訪問 mark[u]=mark[v]=true; in[v]++, out[u]++; u=findset(u), v=findset(v); if(u!=v) fa[u]=v; //合併連通分量 } int start=edges[0].u;//尤拉路徑起始點的編號 int i,c1=0,c2=0; //錯誤1,這裡c1與c2忘了初始化 for(i=0;i<26;i++)if(mark[i]) { if(in[i]==out[i]) continue; else if(in[i]-out[i]==1) c1++; else if(out[i]-in[i]==1) c2++,start=i; else break; } if(i==26&&((c1==c2&&c1==0)||(c1==c2&&c1==1)) && ok() )//所有點度符合要求且弱連通 { cnt=0; euler(start); printf("%s",words[ans[cnt-1]].s); for(int i=cnt-2;i>=0;i--) printf(".%s",words[ans[i]].s); printf("\n"); } else printf("***\n"); } return 0; }