【图论】欧拉图

本文详细介绍了欧拉图的基本概念及欧拉回路、欧拉通路的相关定理,并通过多个实例展示了如何判断一个图是否包含欧拉回路或通路,以及如何找到具体的路径。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

所谓的欧拉图,就是我们小时候经常玩的一种"一笔画"游戏,从任意一个点出发,经过所有的边。而我们将这个模型抽象出来,对边和起点加了不同的限制,就得到了不同的定义。

欧拉通路: 通过图中每条边且只通过一次,并且经过每一顶点的通路。
欧拉回路: 通过图中每条边且只通过一次,并且经过每一顶点的回路。


欧拉回路相关定理: 
1、无向图为欧拉图(欧拉回路),当且仅当为连通图且所有顶点的度为偶数。 
2、无向图为半欧拉图(欧拉通路),当且仅当为连通图且除了两个顶点的度为奇数之外,其它所有顶点的度为偶数。 
3、有向图为欧拉图(欧拉回路),当且仅当的基图连通,且所有顶点的入度等于出度。(忽略有向图所有边的方向,得到的无向图称为该有向图的基图。) 
4、有向图为半欧拉图(欧拉通路),当且仅当的基图连通,且存在顶点的入度比出度大1、的入度比出度小1,其它所有顶点的入度等于出度。 




求无向图的欧拉回路:

Hdu1878 

题意:欧拉回路是指不令笔离开纸面,可画过图中每条边仅一次,且可以回到起点的一条回路。现给定一个图,问是否存在欧拉回路?若欧拉回路存在则输出1,否则输出0。
思路::1、底图是连通的,可用并查集判断。2、不存在度数为奇数的点。 

#include <cstdio>
#include <cstring>
const int N=1005;
int father[N],degree[N];
int find (int x)
{
	if (x==father[x]) return x;
	return father[x]=find(father[x]);
}
int main ()
{
	int n,m,i;
	while (scanf("%d",&n),n)
	{
		scanf("%d",&m);
		for (i=1;i<=n;i++)
			father[i]=i;
		memset(degree,0,sizeof(degree));
		while (m--)
		{
			int a,b;
			scanf("%d%d",&a,&b);
			father[find(b)]=find(a);
			degree[a]++;
			degree[b]++;
		}
		int p=father[1],flag=1;
		for (i=1;i<=n;i++)
			if (father[i]!=p||degree[i]&1)
			{
				flag=0;
				break;
			}
		printf("%d\n",flag);
	}
	return 0;
}


求无向图的欧拉通路: 
POJ1300
题意:有n个房间,每个房间有若干个门和别的房间相连,管家从m房间开始走,要回到自己的住处(0),问是否有一条路可以走遍所有的门并且没有重复的路。
思路:如果没有奇数门的房间,起始点为0,则存在欧拉回路。可以回到住处0。如果有2个奇数门的房间,则存在欧拉通路,并且只有房间0和起始点的房门数量为奇数,这样才可能回到住处0。 

using namespace std;
typedef long long ll;
int main()
{
    char a[M];
    int m,n;
    while(cin>>a)
    {
        else cin>>m>>n;
        =
        /*
        读取数据,door[i]表示第i个房间门的数量 
		*/ 
        for(i=0;i<n;i++)
        {
            if(door[i]&1) odd++;  //看看有多少个点度数是奇点……
        }
        if(odd==0&&m==0) printf("YES %d\n",num); //如果没有奇点,而起始点是0的可以最后回到0点符合,如果第一个样例
        else if(odd==2&&door[m]&1&&door[0]&1&&m!=0)  //如果有2个奇点,且这两个起始点是0和另一个,并且起始点是另一个才能回到0点,如第三个样例
            printf("YES %d\n",num);
        else printf("NO\n");  //第二个样例
    }
    return 0;
}

求有向图的欧拉通路:
POJ1386
题意:给你一些字符串,字符串末位置如果和另一个字符串的首位置相同的话就可以相连 。然后问你是否可以全部连起来。
思路:就是取出每个字符串的首尾位置的字母作为点,然后求出出度和入度,根据有向欧拉通路的性质,可以求出是否可以组成欧拉通路或者欧拉回路。并查集判断是否是一个连通图。

#include <iostream>
#include <cstdio>
#include <cstring>
#include <algorithm>
#define clr(a) memset(a , 0 , sizeof(a) )
using namespace std ;
char a[1111] ;
struct kdq{
    int s,e;
}al[111111] ;
int in[30] ,out[30];
bool vis[30] ;
int fa[30] ;
int find(int x){
    return fa[x] == x ? x : fa[x] = find(fa[x]) ;
}


void Union(int a, int b){
    a = find(a) ;
    b = find(b) ;
    if(a == b)return ;
    if(a < b)fa[b] = a ;
    else fa[a] = b ;
}


void init(){
    for (int i = 0 ; i <= 26; i ++ )fa[i] = i ;
    clr(in) ;
    clr(out) ;
    clr(vis) ;
}


int main(){
    int T ;
    cin >> T ;
    while( T -- ){
        int n ;
        cin >> n ;
        init() ;
        for (int i = 1 ; i <= n ; i ++ ){
            scanf("%s",a) ;
            int l = strlen(a) ;
            al[i].s = a[0] - 'a';
            al[i].e = a[l - 1] - 'a';
            in[a[0] - 'a'] ++ ;
            out[a[l - 1] - 'a'] ++ ;
            vis[a[0] - 'a'] = 1 ;
            vis[a[l - 1] - 'a'] = 1 ;
        }
        for (int i = 1 ; i <= n ; i ++ ){
            int u = al[i].s ;
            int v = al[i].e ;
            Union(u , v) ;
        }
        int jihe = -1 ;
        bool flag = 0 ;
        for (int i = 0 ; i < 26 ; i ++ ){
            if(!vis[i])continue ;
            if(jihe == -1){
                jihe = find(i) ;
            }
            else if(find(i) != jihe){
                flag = 1 ;
                break ;
            }
        }
        int num = 0 ;
        for (int i = 0 ; i < 26 ;i ++ ){
            if(!vis[i])continue ;
            if(abs(in[i] - out[i]) >= 2){//如果入度和出度相差2以上,则不可能存在回路或者通路,因为有向图的欧拉通路/回路中没有这样的节点。 
                flag = 1 ;
                break ;
            }
            else if(abs(in[i] - out[i]) == 1){//如果入度和出度相差1,那么则要记录这样的点有几个。如果1个出度比入度大1,1个出度比入度小1,则构成欧拉通路。 
                num ++ ;
            }
        }
        if(flag){
            puts("The door cannot be opened.") ;
        }else
        if(num == 0 || num == 2 ){//如果没有入度和初度相差1的节点,则说明全都是偶数度节点,存在欧拉回路。 
            puts("Ordering is possible.") ;
        }else {
            puts("The door cannot be opened.") ;
        }
    }
    return 0 ;
}



求有向图的欧拉回路:
哈理工oj 1633 
题意:跟上面那道题差不多,不过要求把全部单词连接起来并能形成环状。是上一道题的一个子问题
#include<stdio.h>  
#include<string.h>  
using namespace std;  
int in[105];  
int out[105];  
int vis[105];  
int main()  
{  
    int t;  
    scanf("%d",&t);  
    while(t--)  
    {  
        memset(in,0,sizeof(in));  
        memset(out,0,sizeof(out));  
        memset(vis,0,sizeof(vis));  
        int n;  
        scanf("%d",&n);  
        for(int i=0;i<n;i++)  
        {  
            char str[15];  
            scanf("%s",str);  
            int x=str[0]-'a';  
            int y=str[strlen(str)-1]-'a';  
            in[x]++;  
            out[y]++;  
            vis[x]=1;  
            vis[y]=1;  
        }  
        int zhongdian=0;  
        int qidian=0;  
        int cont=0;  
        for(int i=0;i<30;i++)  
        {  
            if(vis[i]==1)  
            {  
                if(in[i]!=out[i])  
                {  
                    if(in[i]-out[i]==1)  
                    {  
                        zhongdian++;  
                    }  
                    if(out[i]-in[i]==1)  
                    {  
                        qidian++;  
                    }  
                    cont++;  
                }  
            }  
        }  
        if(cont==0)  
        {  
            printf("circle\n");  
        }  
        else if(cont==2)  
        {  
            if(zhongdian==1&&qidian==1)  
            {  
                printf("path\n");  
            }  
            else  
            {  
                printf("oh no\n");  
            }  
        }  
        else  
        {  
            printf("oh no\n");  
        }  
    }  
}  





输出路径: 
设G 为一无向欧拉图,求G 中一条欧拉回路的算法为:
1) 任取G 中一顶点v0,令P0 = v0;
2) 假设沿Pi = v0e1v1e2v2 …eivi 走到顶点vi,按下面方法从E(G) - { e1, e2, …, ei }中选ei+1:
a) ei+1 与vi 相关联;
b) 除非无别的边可供选择,否则ei+1 不应该是Gi = G - { e1, e2, …, ei }中的桥。
3) 当2)不能再进行时算法停止。
可以证明的是,当算法停止时,所得到的简单回路Pm = v0e1v1e2v2 …emvm, (vm = v0)为G 中一条欧拉回路


题意:
给出一些字符串,问能否将这些字符串  按照 词语接龙,首尾相接  的规则 使得每个字符串出现一次
如果可以 按字典序输出这个字符串序列
#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
const int maxn=1000+10;
struct Word
{
    int len;
    char s[20+5];
    bool operator <(const Word &b)const
    {
        return strcmp(s,b.s)<0;
    }
}words[maxn];
struct Edge
{
    int u,v;//u表示起点,v表示终点
    bool vis;
}edges[maxn];
int fa[26+5];          //并查集
int in[26+5],out[26+5];//入度,出度
bool mark[26+5];       //mark[i]=true,表i这个字母被用到作为图顶点了
int m;//单词数,即边数
int cnt,ans[maxn];//记录输出单词的序号,逆序
int findset(int u)
{
    if(fa[u]==-1) return u;
    return fa[u]=findset(fa[u]);
}
bool ok()//判断是否弱连通图
{
    int k=0;
    for(int i=0;i<26;i++)if(mark[i])
        if(findset(i)==i) k++;
    return k==1;
}
void euler(int u)
{
    for(int i=0;i<m;i++)if(!edges[i].vis && edges[i].u==u)//本边未走过且起点吻合
    {
        edges[i].vis=true;
        euler(edges[i].v);
        ans[cnt++]=i;
    }
}
int main()
{
    int T; scanf("%d",&T);
    while(T--)
    {
        scanf("%d",&m);
        for(int i=0;i<m;i++)
        {
            scanf("%s",words[i].s);
            words[i].len=strlen(words[i].s);
        }
        sort(words,words+m);


        memset(fa,-1,sizeof(fa));
        memset(mark,0,sizeof(mark));
        memset(in,0,sizeof(in));
        memset(out,0,sizeof(out));
        for(int i=0;i<m;i++)
        {
            int u,v;
            u=words[i].s[0]-'a';
            v=words[i].s[words[i].len-1]-'a';
            edges[i].u=u;
            edges[i].v=v;
            edges[i].vis=false;     //表示该边还未被访问
            mark[u]=mark[v]=true;
            in[v]++, out[u]++;
            u=findset(u), v=findset(v);
            if(u!=v) fa[u]=v;       //合并连通分量
        }


        int start=edges[0].u;//欧拉路径起始点的编号
        int i,c1=0,c2=0;              
        for(i=0;i<26;i++)
		if(mark[i])
        {
            if(in[i]==out[i]) continue;
            else if(in[i]-out[i]==1) c1++;
            else if(out[i]-in[i]==1) c2++,start=i;
            else break;
        }
        if(i==26&&((c1==c2&&c1==0)||(c1==c2&&c1==1)) && ok() )//所有点度符合要求且弱连通
        {
            cnt=0;
            euler(start);
            printf("%s",words[ans[cnt-1]].s);
            for(int i=cnt-2;i>=0;i--)
                printf(".%s",words[ans[i]].s);
            printf("\n");
        }
        else printf("***\n");
    }
    return 0;
}



H:\小视野 信竞题 -对拍\课程二 43-71课\第54课 图论专题 欧拉图\Problem C 相框_std.cpp: In function ‘int main()’: H:\小视野 信竞题 -对拍\课程二 43-71课\第54课 图论专题 欧拉图\Problem C 相框_std.cpp:23:16: error: expected unqualified-id before ‘[’ token for (auto &[a, b] : wires) { ^ H:\小视野 信竞题 -对拍\课程二 43-71课\第54课 图论专题 欧拉图\Problem C 相框_std.cpp:23:16: error: expected ‘;’ before ‘[’ token H:\小视野 信竞题 -对拍\课程二 43-71课\第54课 图论专题 欧拉图\Problem C 相框_std.cpp:23:17: error: ‘a’ was not declared in this scope for (auto &[a, b] : wires) { ^ H:\小视野 信竞题 -对拍\课程二 43-71课\第54课 图论专题 欧拉图\Problem C 相框_std.cpp:23:20: error: ‘b’ was not declared in this scope for (auto &[a, b] : wires) { ^ H:\小视野 信竞题 -对拍\课程二 43-71课\第54课 图论专题 欧拉图\Problem C 相框_std.cpp: In lambda function: H:\小视野 信竞题 -对拍\课程二 43-71课\第54课 图论专题 欧拉图\Problem C 相框_std.cpp:23:23: error: expected ‘{’ before ‘:’ token for (auto &[a, b] : wires) { ^ H:\小视野 信竞题 -对拍\课程二 43-71课\第54课 图论专题 欧拉图\Problem C 相框_std.cpp: In function ‘int main()’: H:\小视野 信竞题 -对拍\课程二 43-71课\第54课 图论专题 欧拉图\Problem C 相框_std.cpp:23:23: error: expected ‘;’ before ‘:’ token H:\小视野 信竞题 -对拍\课程二 43-71课\第54课 图论专题 欧拉图\Problem C 相框_std.cpp:23:23: error: expected primary-expression before ‘:’ token H:\小视野 信竞题 -对拍\课程二 43-71课\第54课 图论专题 欧拉图\Problem C 相框_std.cpp:23:23: error: expected ‘)’ before ‘:’ token H:\小视野 信竞题 -对拍\课程二 43-71课\第54课 图论专题 欧拉图\Problem C 相框_std.cpp:23:23: error: expected primary-expression before ‘:’ token H:\小视野 信竞题 -对拍\课程二 43-71课\第54课 图论专题 欧拉图\Problem C 相框_std.cpp:33:15: error: expected unqualified-id before ‘[’ token for (auto [a, b] : wires) { ^ H:\小视野 信竞题 -对拍\课程二 43-71课\第54课 图论专题 欧拉图\Problem C 相框_std.cpp:33:15: error: expected ‘;’ before ‘[’ token H:\小视野 信竞题 -对拍\课程二 43-71课\第54课 图论专题 欧拉图\Problem C 相框_std.cpp:33:16: error: ‘a’ was not declared in this scope for (auto [a, b] : wires) { ^ H:\小视野 信竞题 -对拍\课程二 43-71课\第54课 图论专题 欧拉图\Problem C 相框_std.cpp:33:19: error: ‘b’ was not declared in this scope for (auto [a, b] : wires) { ^ H:\小视野 信竞题 -对拍\课程二 43-71课\第54课 图论专题 欧拉图\Problem C 相框_std.cpp: In lambda function: H:\小视野 信竞
03-28
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值