poj 2337
题意:给你一些单词,能否将这些单词首尾连接起来,即前一个单词的最后一个字母和后一个单词的第一个字母相同,若能输出该序列,若有多种则按字典序输出该序列。
如果将每一个单词的首字母和尾字母看成节点,每个单词看成一个线段的话,若满足上述条件,就会构成一个欧拉图,然后就找出符合条件的欧拉路径。
(1)构图。以为要按字典序输出,所以单词输入完毕后,进行一趟排序,有大到小排,若采用头插法,邻接表建立后,后面的节点就会自然就会按字典序排好。
(2)判断有没有欧拉路径。作为有向图,有欧拉路径的从分条件是:在连通的前提下,始点的出度比入度大一且终点的入度比出度大一且其他的顶点出度等于入度,或者所有顶点出度等于入度。
(3)输出欧拉路径。用fleury算法需要找始点,这个很关键,还有一点也很关键,
比如说一个图:a->b,b->c,a->d,d->a;
在使用freury算法的过程中当到达了a接下来就继续选择一条边删除。那么选择那一条边呢?若选择a到b这条,那么到达b后ab边删除了,再也到达不了a了,这样就悲剧了。所以在选择边的时候,如果a只有一个临接点,这种情况下只有选着那一条边了。若a有多个临接点,就需要选择一条能够返回a的那一条边,上图ad就是这样的边,这就需要一个递归函数来判断了,这一点纠结了很久,以下是代码,感觉写的不是太好。
代码如下:
#include<iostream> #include<stack> #include<cstring> #include<cstdlib> using namespace std; struct node { int i; char s[25]; node * next; }; node * head[101]; char str[101][25],letter[26]; int indegree[26],outdegree[26],father[26],wight[26],sum,visit[26]; stack <int> S; int merge(int s,int t) { if(wight[s]>=wight[t]) { father[t]=s; wight[s]+=wight[t]; } else { father[s]=t; wight[t]+=wight[s]; } return 0; } int find(int i) { while(father[i]!=i) i=father[i]; return i; } int search(char c) { int i; for(i=0;i<sum;i++) if(letter[i]==c) return i; letter[sum]=c; return sum++; } int insert(int s,int t,char * ss) { node* p=new node; p->i=t; strcpy(p->s,ss); p->next=head[s]; head[s]=p; return 0; } int deleteNode() { int i; for(i=0;i<26;i++) { node* p=head[i]; while(p) { head[i]=p->next; delete p; p=head[i]; } } return 0; } int cmp(const void * s,const void * t) { return strcmp((char*)t,(char*)s); } node* dfs(int i,int n) { if(i==n) return head[i]; node* p=head[i]; visit[i]=1; while(p) { if(!visit[p->i] && dfs(p->i,n)!=NULL) return p; p=p->next; } visit[i]=0; return NULL; } node* judge(int i) { node * p=head[i]; if(p->next==NULL) return p; memset(visit,0,sizeof(visit)); while(p) { if(dfs(p->i,i)!=NULL) return p; p=p->next; } return NULL; } int Delete(int i,node* p) { node* q=head[i]; if(head[i]==p) { head[i]=p->next; delete p; return 0; } while(q->next && q->next!=p) { q=q->next; } if(q->next) { q->next=p->next; delete p; } return 0; } int main() { int i,m,n,s,t,count,in,out,equal,e,l,start,end,ss,tt; cin>>n; while(n--) { cin>>m; for(i=0;i<m;i++) cin>>str[i]; qsort(str,m,sizeof(str[0]),cmp); for(i=0;i<26;i++) head[i]=NULL; memset(outdegree,0,sizeof(outdegree)); memset(indegree,0,sizeof(indegree)); sum=0; for(i=0;i<26;i++) { father[i]=i; wight[i]=1; } for(i=0;i<m;i++) { s=search(str[i][0]); ss=strlen(str[i])-1; t=search(str[i][ss]); outdegree[s]++;indegree[t]++; insert(s,t,str[i]); ss=find(s); tt=find(t); merge(ss,tt); } //判断连通性 count=0; for(i=0;i<sum;i++) if(father[i]==i) count++; if(count>1) { cout<<"***"<<endl; continue; } //判断有无欧拉路径 in=out=equal=0; for(i=0;i<sum;i++) { if(outdegree[i]-indegree[i]==1) { out++;start=i; } else if(indegree[i]-outdegree[i]==1) { in++;end=i; } else if(indegree[i]==outdegree[i]) { equal++; } } if(!((out==1 && in==1 && equal==sum-2)||equal==sum)) { cout<<"***"<<endl; continue; } //找始点 if(equal==sum) for(i=0;i<sum;i++) if(str[m-1][0]==letter[i]) { start=i; break; } S.push(start);l=0; while(!S.empty()) { e=S.top();S.pop(); node* p=head[e]; if(p) { node* k=judge(e);//选择合适的边 S.push(k->i); strcpy(str[l++],k->s); Delete(e,k);//删除边 } } while(!S.empty()) S.pop(); for(i=0;i<l-1;i++) cout<<str[i]<<"."; cout<<str[i]<<endl; deleteNode(); } return 0; }