图论之欧拉图

欧拉路径/欧拉回路

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

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

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

 

 uva 10054

 思路:判断给出的边是否构成欧拉回路,由于点可以重复,且点的数量(<=50)比较小,所以我们直接用50*50的图去存边的情况

#include <bits/stdc++.h>
using namespace std;
#define maxn 51
int G[maxn][maxn],du[maxn],m;
int ans[1001][2];//存u,v 
int sum=0; 
void dfs(int u)
{
    for(int i=1;i<=50;i++)
    {
        if(G[u][i]==0)
        continue;
        G[u][i]--;G[i][u]--;
        dfs(i);
        sum++;
        ans[sum][0]=u;
        ans[sum][1]=i;
    }
}
void solve(int t)
{
    int u,v;
    sum=0;
    memset(G,0,sizeof(G));
    memset(du,0,sizeof(du));
    
    for(int i=1;i<=m;i++)
    {
    scanf("%d %d",&u,&v);
    G[u][v]++;G[v][u]++;
    du[u]++;du[v]++;
    }
    printf("Case #%d\n",t);
    for(int i=1;i<=50;i++)
    {
        if(du[i]%2==1)
        {
        printf("some beads may be lost\n");
        return;
        }
    }
    for(int i=1;i<=50;i++)
    {
        if(du[i])
        {
            dfs(i);
            break;
        }    
    }
    if(sum!=m)
    {
        printf("some beads may be lost\n");
        return;
    }
    for(int i=sum;i>=1;i--)
    printf("%d %d\n",ans[i][0],ans[i][1]);
}
int main()
{
    int T;
    scanf("%d",&T);
    for(int t=1;t<=T;t++)
    {
        scanf("%d",&m);
        solve(t);
        if(t!=T)
        printf("\n");
    }
}
View Code

 

 poj 2230---简单题

思路:构建有向图,判断是否为欧拉图

//#include <bits/stdc++.h>
#include<iostream>
#include<algorithm>
#include<cstring>
#include<cstdio>
using namespace std;
#define maxn 10006
struct node{
    int v,vis,p;
}G[maxn*10];
int m,n,head[maxn*10],cnt=0,sum=0,ans[maxn*10];
void add(int u,int v)
{
    G[++cnt]=node{v,0,head[u]};
    head[u]=cnt;
}
void dfs(int u)
{
    for(int i=head[u];i!=-1;i=head[u])
    {
        head[u]=G[i].p;
        if(G[i].vis)
        continue;
        G[i].vis=1;
        dfs(G[i].v);
        ans[++sum]=G[i].v;
    }
}
int main()
{
    int u,v;
    scanf("%d%d",&n,&m);
    memset(head,-1,sizeof(head));
    for(int i=1;i<=m;i++)
    {
        scanf("%d%d",&u,&v);
        add(u,v);add(v,u);
    }
    dfs(1);//题目要求开始和结束都为1 
    ans[++sum]=1;    
    if(sum==m*2+1)
    {
    for(int i=1;i<=sum;i++)
    cout<<ans[i]<<endl;
    }    
}
View Code

 

luogu1341 无序字母对

找一个无向图中的字典序最小的欧拉路径。我只要做的时候按照字典序去遍历边就好了

#include<bits/stdc++.h>
using namespace std;
#define maxn 700
int  inn[maxn],sum=0;
int s[maxn], G[maxn][maxn];

int judge(char x){
    if(x<='z'&&x>='a') return x-'a'+27;
    else return x-'A'+1;
}
void Eular(int x){
    for(int i=1;i<=52;i++)
        if(G[x][i]){
            G[x][i]=G[i][x]=0;
            Eular(i);
        }
    s[++sum]=x;
}
int main(){
    int n,cnt=0;
    cin>>n;
    int u,v;
    char ss[5];
    memset(inn,0,sizeof(inn));
    for(int i=1;i<=n;i++){
        scanf("%s",ss);          
        u=judge(ss[0]);v=judge(ss[1]);
        inn[u]++; inn[v]++;
        G[u][v]=1;G[v][u]=1;
    }
    int p=2e9;//改一下 
    for(int i=1;i<=52;i++){
        if(inn[i]&1){
            p=min(p,i); ++cnt;
        }
    }
    if(cnt!=0 && cnt!=2){
        printf("No Solution\n"); return 0;
    }
    if(cnt==0)
    {
        for(int i=1;i<=52;i++) 
        if(inn[i]){
        p=i; break;
    }
    }
    sum=0; 
    Eular(p);
    for(int i=sum;i>=1;i--) 
    {
        int x=s[i];char ch;
        if(x<=26) ch='A'+x-1;
        else    ch='a'+x-27;
        printf("%c",ch);
    }
    return 0;    
}
View Code

 

转载于:https://www.cnblogs.com/Aiahtwo/p/11414357.html

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值