欧拉回路图上瘾中,总结下用法
对于必须要用上某个单词,就取这个单词的左部分为L,右部分为R,形成一个从L到R的有向图
然后再判断是否符合欧拉回路图,即验证所有点的出度和入度,验证方法如下:
1.如果所有的点的入度都等于本身的出度,则满足欧拉回路
2.只有2个点的出度不等于入度,且一个点in比out多1,另一个点in比out少1
然后找出那个入度比出度多1的点,这个就是我们要找的起点(如果所有点的出度都等于入度,就随便取个点)
然后进行Fleury(起点)算法,算出路径,和r值
r值可以用来检测图的连通性,r的含义是访问的点的数量,所以如果整个图都是连通的,那么r必然等于n+1
否则就说明图并不只一个连通图,那么输出NO
还有个细节就是感觉递归次数多了点,,加上扩栈比较好
刚开始是用vector加vis标记边被删除的写法,但是超时了
后来发现,如果是200000个zzz,那么vector里的边并没有删除,所以还是要循环n^2次,复杂度退化到O(n^2)
然后想利用vector删除节点的方法做,开始以为vector是链表形式,在哪里删除效率都一样,所以每次循环都删除第一个,又超时了
改成每次循环先考虑最后一个,再把最后一个删除,这样做不仅删除的复杂度是O(1),而且代码比以前更简单,又一次见识到了vector并没有别人说的那么慢,反而感觉很好很强大
贴上丑陋的代码,Fleury的代码真心短,主要代码长度写在check函数上,稍微啰嗦了点
#include<cstdio> #include<cstdlib> #include<cstring> #include<cmath> #include<queue> #include<algorithm> #pragma comment(linker,"/STACK:102400000,102400000") using namespace std; typedef long long LL; const int mod=1e9+7; const int MX=250000+5; const int INF=0x3f3f3f3f; int Path[MX],r=0; int IN[80000],OUT[80000]; vector<int>G[80000]; char ans[MX]; int Get(char*s){ return s[0]*255+s[1]; } int check(){ int a=0,b=0,c=0,r1=-1,r2=-1; for(int j=0;j<=255;j++){ for(int k=0;k<=255;k++){ int i=j*255+k; if(!IN[i]&&!OUT[i]) continue; r1=i; if(IN[i]==OUT[i]) continue; a++; if(IN[i]==OUT[i]+1){ b++; r2=i; } if(IN[i]+1==OUT[i]) c++; } } if(a==0){ return r1; }else if(a==2&&b==1&&c==1){ return r2; }else{ return -1; } } void Fleury(int u){ int s=G[u].size(); while(s){ int v=*(G[u].end()-1); G[u].erase(G[u].end()-1); Fleury(v); s=G[u].size(); } Path[++r]=u; } int main(){ memset(IN,0,sizeof(IN)); memset(OUT,0,sizeof(OUT)); int n;char word[10]; scanf("%d",&n); for(int i=1;i<=n;i++){ scanf("%s",word); int L=Get(word),R=Get(word+1); IN[L]++;OUT[R]++; G[L].push_back(R); } int Beg=check(); if(Beg==-1){ printf("NO\n"); return 0; } Fleury(Beg); if(r!=n+1){ printf("NO\n"); return 0; } printf("YES\n"); printf("%c%c",Path[r]/255,Path[r]%255); for(int i=r-1;i>=1;i--){ printf("%c",Path[i]%255); } return 0; }