欧拉回路图上瘾中,总结下用法
对于必须要用上某个单词,就取这个单词的左部分为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;
}