poj 2337 Catenyms
再学欧拉路
无力再写题解报告了,最近写得最难受的一道题,前前后后调试了有10来个小时,就一个这么个BUG
#define N 30
#define MAX 1010
开某些数组的时候把N和MAX写反了
但是…………代码还是有问题的,G++一直过不了,一直是WA,C++可以过,等下还要调试,一定要把G++给过了
题意:和poj 1386 是一样的题目,不过这次要输出路径,而且要字典序最小
做法:用邻接表来构建有向图(我的构建方法和网上找来的不一样,不是用白书介绍的那种模拟链表的头插法,而是直接一点,比较然后插入,已有的元素向后移,感觉在时间上没什么差距)。然后用并查集来判断有向图的基图是否连通(其实直接用邻接矩阵再DFS来判断连通也行,这里最多就26个顶点,要判断基图连通其实所用时间也差不多吧,反正不会TLE什么的),同时要记录每个点的入度和出度,然后还要判断度是否符合欧拉路的要求,最后DFS输出路径
不多说了,无力写了,很多内容都已经在代码的注释中
#include <stdio.h> #include <string.h> #include <cmath> //#include <algorithm> //using namespace std; #define N 30 //顶点个数,即最多26个字母 #define MAX 1010 //单词个数最多1000 #define LEN 25 //单词长度最多为20 char word[N][LEN]; //单词表 bool w[MAX]; //标记哪个单词被用过 int in[N],out[N]; //26个顶点的入度和出度 struct edgenode //邻接表的节点 { int e,v; //e是第几条边也就是单词表中的第e个单词,v是有向边<u,v>的顶点v }; struct node { int c; //有向边<u,v>的顶点u一共有c条边,也就是邻接表的长度 struct edgenode b[MAX]; //邻接表 }a[N]; //26个顶点 int minnum; //字典序最小的那个单词的编号 int start,eend; //记录欧拉道路的起点和终点 //bool g[N][N],vis[N]; //构建基图判断图连通 int n; int stack[MAX],top; //用于路径保存 int father[N]; void init() { for(int i=1; i<=26; i++) father[i]=i; //memset(g,0,sizeof(g)); //memset(vis,0,sizeof(vis)); memset(in,0,sizeof(in)); memset(out,0,sizeof(out)); memset(a,0,sizeof(a)); memset(w,0,sizeof(w)); memset(stack,0,sizeof(stack)); top=-1; return ; } void addedge(int u ,int v , int e) //第e条有向边(即第e个单词)<u,v> { int i,j; struct edgenode tmp; tmp.e=e; tmp.v=v; for(i=1; i<=a[u].c; ) if( strcmp( word[e] , word[a[u].b[i].e]) >= 0 ) i++; else //在i位置插入 break; for(j=a[u].c; j>=i; j--) a[u].b[j+1]=a[u].b[j]; a[u].b[i]=tmp; a[u].c++; return ; } int find(int x) { return father[x]==x ? x : (father[x]=find(father[x]) ); } void SET(int u , int v) { int x,y; x=find(u); y=find(v); if(x!=y) father[x]=y; return ; } void input() { int i,j,u,v,len,x,y; scanf("%d",&n); for(minnum=1, i=1; i<=n; i++) { scanf("%s",word[i]); len=strlen(word[i]); u=word[i][0]-'a'+1; v=word[i][len-1]-'a'+1; //得到有向边<u,v> addedge(u,v,i); //往点u中添加第i条有向边<u,v> out[u]++; in[v]++; //g[u][v]=g[v][u]=1; //构建基图 SET(u,v); if( strcmp(word[i],word[minnum])<0 ) minnum=i; //记录字典序最小的单词 } return ; } int Degree() //判断图的度是否满足要求 { int i,n1,n2; n1=n2=0; start=eend=0; for(i=1; i<=26; i++) { if(out[i]-in[i]==1) { n1++; if(n1>1) break; start=i; } else if(in[i]-out[i]==1) { n2++; if(n2>1) break; eend=i; } else if( abs(in[i]-out[i])>1 ) break; } if(i<=26) return 0; //没有完全扫描整个数组说明不存在欧拉路 else if(n1==0 && n2==0) //可以从任意顶点出发 { start=word[minnum][0]-'a'+1; return 1; } else if(n1==1 && n2==1) return 1; else return 0; } void dfs(int u ,int ee) { int i,e,v; for(i=1; i<=a[u].c; i++) //扫描点u的邻接表从尾部扫描回来 { e=a[u].b[i].e; //取出这条边 v=a[u].b[i].v; //取出有向边<u,v>的另一个顶点v if(!w[e]) { w[e]=1; dfs(v,e); //递归顶点v; } } stack[++top]=ee; return ; } void printfff() { int i; for(i=top-1; i>=0; i--) if(i!=0) printf("%s.",word[stack[i]]); else printf("%s\n",word[stack[i]]); } int connect() { int tmp,i,count; //并查集判断图连通,不能超过1个顶点和它的双亲father[i]相同 //超过1过说明不连通 for(count=0,i=1; i<=26; i++) if(in[i] || out[i]) if( father[i]==i ) count++; if(count>1) return 0; else return 1; } int main() { int T,tmp; scanf("%d",&T); while(T--) { init(); input(); if(connect()) //图连通 { if(Degree()) //存在欧拉路径 { dfs(start,0); printfff(); } else //度不符合 { //printf("图连通但是度不符合\n"); printf("***\n"); } } else { //printf("图不连通\n"); printf("***\n"); } } return 0; }