欧拉路径/欧拉回路
欧拉路径是一条经过图中所有边且只经过一次的路径(类似于一笔画问题);
欧拉回路的话就是起点和终点相同的欧拉路径
欧拉通路(欧拉路径):S点到T点的路径经过图中所有的边,有且仅有一次
欧拉回路:就是起点和终点相同的欧拉路径
欧拉图:通过图(无向图或有向图)中所有边且每边仅通过一次通路,相应的回路称为欧拉回路
下图p1,p2都不是欧拉图:因为不存在有一条路径能通过所有边且边只经过一次
而下图可以被称为欧拉图:
性质
1.无向连通图 G 是欧拉图,当且仅当 G 不含奇数度结点( G 的所有结点度数为偶数);
2.无向连通图G 含有欧拉通路,当且仅当 G 有零个或两个奇数度的结点;
3.有向连通图 D 是欧拉图,当且仅当该图为连通图且 D 中每个结点的入度=出度;
4.有向连通图 D 含有欧拉通路,当且仅当该图为连通图且 D 中除两个结点外,其余每个结点的入度=出度,且此两点满足 deg-(u)-deg+(v)=±1 。(起始点s的入度=出度-1,结束点t的出度=入度-1 或两个点的
入度=出度);
5.一个非平凡连通图是欧拉图当且仅当它的每条边属于奇数个环;
6.如果图G是欧拉图且 H = G-uv,则 H 有奇数个 u,v-迹仅在最后访问 v ;同时,在这一序列的 u,v-迹中,不是路径的迹的条数是偶数。
求法
对于无向图满足以下条件说明该图为无向欧拉图
1.图中的所有边的度数都为偶数
2.路径边的数目为图中边的数目
求法:
第一步:输入点的数目,边的数目,输入每条边的起点和终点,记录两个点的度数,边的信息
第二步:判断每个点的度数是否为奇数,若存在奇数该图不可能为欧拉图,不存在则继续判断
第三步:找到一个有边的点,从该点作为路径的起点进行dfs
第四步:dfs:进行前向星的遍历,回溯之后记录答案
第五步:判断路径边的数目是否和图中边的数目相等,若不相等该图不为欧拉图,相等则输出欧拉路径
对于有向图满足以下条件说明该图为有向欧拉图
1.图中的所有点的入度数等于出度数
2.路径边的数目为图中边的数目
求法:
第一步:输入点的数目,边的数目,输入每条边的起点和终点,记录该点的入度数,出度数,边的信息
第二步:判断每个点的入度数是否不等于出度数,若存在不相等该图不可能为欧拉图,都相等则继续判断
第三步:找到一个有边的点,从该点作为路径的起点进行dfs
第四步:dfs:进行前向星的遍历,回溯之后记录答案
第五步:判断路径边的数目是否和图中边的数目相等,若不相等该图不为欧拉图,相等则输出欧拉路径
例题
UOJ 117 欧拉回路 ***非常经典重要的一道题
题意:求有向图与无向图的欧拉回路,如果存在,输出欧拉回路(边的序号),注意题目中给出的无向图的输出方式
1 //#include<bits/stdc++.h> 2 #include<iostream> 3 #include<string> 4 #include<cstring> 5 inline int read() ///快速读入 6 { 7 int X=0,w=0; 8 char ch=0; 9 while(!isdigit(ch)) 10 { 11 w|=ch=='-'; 12 ch=getchar(); 13 } 14 while(isdigit(ch)) 15 X=(X<<3)+(X<<1)+(ch^48),ch=getchar(); 16 return w?-X:X; 17 } 18 19 int op,n,m;//图的标记,图中点的数目,边的数目 20 struct node 21 { 22 int e;//终点 23 int p;//边的编号 24 int vis;///标记这条边是否被访问过 25 } load[400005]; 26 int head[100005],sign,cnt;//前向星,sign记录此时存图中边数组load存到的边的数目,cnt记录此时存路径点的数组ans存到的边的数目 27 28 void add_edge(int s,int e) //添加起点s终点e的边 29 { 30 load[++sign]=node{e,head[s],0}; 31 head[s]=sign; 32 } 33 34 void init()//初始化 35 { 36 cnt=sign=0; 37 memset(head,-1,sizeof(head)); 38 } 39 40 int ans[200005];//答案数组 41 42 void dfs_unedge(int s)//无向图 43 { 44 for(int i=head[s]; i!=-1; i=head[s]) 45 { 46 ///目的是为了优化,因为每一条边只访问一次/ 47 ///可以将这条边删除掉,可以降低时间复杂度(否则会T) 48 head[s]=load[i].p; 49 50 int e=load[i].e; 51 if(load[i].vis) ///访问过的边不会再次访问 52 continue; 53 ///因为储存的双向边,两条边都需要标记 54 load[i].vis=1;//标记第一条边 55 (i&1)?load[i+1].vis=1:load[i-1].vis=1;//标记第二条边 56 57 dfs_unedge(e); 58 ans[++cnt]=(i+1)/2; ///回溯(说明已经找到了解)后储存答案 59 if((i&1)==0) ///反向走的边变为负值 i是边的序号 无向图存边正向的边序号都是奇数,反向边是在正向边之后存的(所以序号为偶数) 60 ans[cnt]*=-1; 61 } 62 } 63 64 void solve_unedge() //无向图欧拉回路 65 { 66 int de[100005],s,e;//记录每个点的度数 67 memset(de,0,sizeof(de)); 68 n=read();//点的数目 69 m=read();//边的数目 70 for(int i=1; i<=m; i++) 71 { 72 s=read(); 73 e=read(); 74 de[s]++; 75 de[e]++; 76 ///建立双向边 77 add_edge(s,e); 78 add_edge(e,s); 79 } 80 ///对于无向图来说度数为奇数的点个数为0 81 for(int i=1; i<=n; i++) 82 { 83 if(de[i]&1) 84 { 85 printf("NO\n"); 86 return ; 87 } 88 } 89 for(int i=1; i<=n; i++) 90 { 91 if(de[i])//找一条有边的点去找欧拉回路 92 { 93 dfs_unedge(i); 94 break; 95 } 96 } 97 if(cnt!=m)//路径边的数目不等于图中所有的边 98 { 99 printf("NO\n"); 100 return ; 101 } 102 printf("YES\n"); 103 ///逆序输出,因为方向正负已经决定了 104 for(int i=cnt; i>=1; i--)//输出图中的欧拉回路 105 printf("%d ",ans[i]); 106 } 107 108 void dfs_diredge(int s) //有向图欧拉回路 109 { 110 for(int i=head[s]; i!=-1; i=head[s]) 111 { 112 head[s]=load[i].p; 113 int e=load[i].e; 114 if(load[i].vis) 115 continue; 116 load[i].vis=1; 117 dfs_diredge(e); 118 ans[++cnt]=i;//回溯后存答案 119 } 120 } 121 122 void solve_diredge() ///有向图欧拉回路 123 { 124 int in[100005],out[100005]; 125 n=read(); 126 m=read(); 127 for(int i=1; i<=n; i++) 128 in[i]=out[i]=0; 129 int s,e; 130 for(int i=1; i<=m; i++) 131 { 132 s=read(); 133 e=read(); 134 out[s]++;//起点的出度 135 in[e]++;//终点的入度 136 add_edge(s,e); 137 } 138 ///对于有向图来说每个点的入度必须等于出度。 139 for(int i=1; i<=n; i++) 140 { 141 if(in[i]-out[i])//说明入度出度不相等 142 { 143 printf("NO\n"); 144 return ; 145 } 146 } 147 for(int i=1; i<=n; i++)//找到第一个有边的点 148 { 149 if(head[i]!=-1) 150 { 151 dfs_diredge(i); 152 break; 153 } 154 } 155 if(cnt!=m)//路径里的边的数目不等于图中边的数目 156 { 157 printf("NO\n"); 158 return ; 159 } 160 printf("YES\n"); 161 for(int i=cnt; i>=1; i--)//输出图中的欧拉回路 162 printf("%d ",ans[i]); 163 } 164 165 int main() 166 { 167 init();///初始化 168 op=read();//图的种类 1为无向图,2为有向图 169 op==1?solve_unedge():solve_diredge(); 170 return 0; 171 }
luogu1341 无序字母对
找一个无向图中的字典序最小的欧拉路径。我只要做的时候按照字典序去遍历边就好了
#include<bits/stdc++.h> #define pa pair<int,int> #define CLR(a,x) memset(a,x,sizeof(a)) using namespace std; typedef long long ll; const int maxn=128,maxm=1e4; inline ll rd(){ ll x=0;char c=getchar();int neg=1; while(c<'0'||c>'9'){if(c=='-') neg=-1;c=getchar();} while(c>='0'&&c<='9') x=x*10+c-'0',c=getchar(); return x*neg; } int N,ans[maxm],e[maxn],pct; bool eg[maxn][maxn]; void dfs(int x){ for(int i='A';i<='z';i++){ if(!eg[x][i]) continue; eg[x][i]=eg[i][x]=0; dfs(i); }ans[++pct]=x; } int main(){ //freopen(".in","r",stdin); int i,j,k; N=rd(); for(i=1;i<=N;i++){ char c[5]; scanf("%s",c); eg[c[0]][c[1]]=eg[c[1]][c[0]]=1; e[c[0]]++,e[c[1]]++; } int cnt=0; for(i='A';i<='z';i++){ if(e[i]&1) cnt++; } if(cnt!=0&&cnt!=2){ printf("No Solution\n"); return 0; } int s=127; for(i='A';i<='z';i++){ if((!cnt&&e[i])||(cnt==2&&(e[i]&1))) s=min(s,i); } dfs(s); for(i=N+1;i;i--){ printf("%c",ans[i]); } return 0; }