图论之欧拉图

柔情痞子 提交于 2019-11-28 15:59:07

欧拉路径/欧拉回路

欧拉路径是一条经过图中所有边且只经过一次的路径(类似于一笔画问题);

欧拉回路的话就是起点和终点相同的欧拉路径

欧拉通路(欧拉路径):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;
}
View Code

 

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!