欧拉路问题

Posted chdy

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了欧拉路问题相关的知识,希望对你有一定的参考价值。

euler路问题也称一笔画问题。

1.一张无向图,若存在一条从节点s到节点t的路径,恰好不重不漏地经过每条边一次(可以重复经过图中节点,最终回到节点s。

这条路径称该路径为s到t的euler回路。其实通过图中所有边的简单路就叫euler路。

2.特别的,如果存在一条从s出发的路径,恰好不重不漏地经过每条边一次最终回到s。

该条路径称为euler回路。其实就是闭合的欧拉路。

3.euler图:一张无向图,且无向图连通,每个点的度数都是偶数。其实就是包含euler回路的图。

4.euler路的判定:图中恰好有两个点度数为奇数,其他节点的度数为偶数。这两个度数为奇数的点就是起点与终点了。

5.euler回路的判定:图中所有点的度数都是偶数,任意点都是起点和终点。

dfs求出euler回路:

技术分享图片

这道题保证是一个euler回路或者是euler路了,或者是euler回路加euler路,所以不需要再判断是否存在,直接求出字典序最小的答案即可。

首先是要看出起点应该在哪,发现如果有度数是奇数点的话起点就是最小的奇数点,而度数都是偶数点的话起点就在最小的偶数点。

数据范围小所以考虑直接邻接矩阵存储,然后字典序好得关键是答案输出的问题,回溯之后存答案,而不是回溯之前,为什么?

技术分享图片

可以看出直接输出是不对的两个环套在一块,直接输出发现这个图连不起来了,所以回溯之后存。

究竟是什么原因:个人理解是由于如上图两环套在一起,一定要把第二个环完全嵌入第一个环之中,也就是说当你跑完第一个环时,第二个环还没跑,你要在跑完第一个环之前把第二个环给跑了才行。所以直接输出是不对滴~

技术分享图片
#include<iostream>
#include<cstring>
#include<string>
#include<ctime>
#include<cmath>
#include<iomanip>
#include<algorithm>
#include<queue>
#include<vector>
#include<stack>
#include<cstdio>
#include<map>
#include<deque>
#include<set>
using namespace std;
inline int read()
{
    int x=0,f=1;char ch=getchar();
    while(ch<0||ch>9){if(ch==-)f=-1;ch=getchar();}
    while(ch>=0&&ch<=9){x=x*10+ch-0;ch=getchar();}
    return x*f;
}
void put(int x)
{
    if(x==0){putchar(0);putchar(
);return;}
    if(x<0)x=-x,putchar(-);
    int num=0;char ch[50];
    while(x)ch[++num]=x%10+0,x=x/10;
    while(num)putchar(ch[num--]);
    putchar(
);return;
}
const int maxn=4002;
int n,m=0;
int a[maxn][maxn];
int b[maxn],len=0,minn=maxn,ru[maxn];
void dfs(int x)
{
    for(int i=1;i<=m;i++)
        if(a[i][x]!=0)
        {
            a[i][x]--;a[x][i]--;
            dfs(i);b[++len]=i;
        }
}
int main()
{
    //freopen("1.in","r",stdin);
    n=read();
    for(int i=1;i<=n;i++)
    {
        int x,y;
        x=read();y=read();
        a[x][y]++;a[y][x]++;
        m=max(m,max(x,y));
        minn=min(minn,min(x,y));
        ru[x]++;ru[y]++;
    }
    //cout<<minn<<endl;
    for(int i=1;i<=m;i++)if(ru[i]%2==1){minn=i;break;}
    dfs(minn);
    b[++len]=minn;
    for(int i=len;i>=1;i--)put(b[i]);
    return 0;
}
View Code

复杂度是O(nm)的,其中n为点数,m为边数。复杂度有点高,况且当图很大是邻接矩阵存不下。

使用邻接表来存图,效率会更高。而且dfs递归乘数是m容易爆栈。再把dfs变成bfs形势的。

如果不要求字典序的话复杂度为O(n+m);

代码:

技术分享图片
#include<iostream>
#include<cstring>
#include<string>
#include<ctime>
#include<cmath>
#include<iomanip>
#include<algorithm>
#include<queue>
#include<vector>
#include<stack>
#include<cstdio>
#include<map>
#include<deque>
#include<set>
using namespace std;
inline int read()
{
    int x=0,f=1;char ch=getchar();
    while(ch<0||ch>9){if(ch==-)f=-1;ch=getchar();}
    while(ch>=0&&ch<=9){x=x*10+ch-0;ch=getchar();}
    return x*f;
}
void put(int x)
{
    if(x==0){putchar(0);putchar(
);return;}
    if(x<0)x=-x,putchar(-);
    int num=0;char ch[50];
    while(x)ch[++num]=x%10+0,x=x/10;
    while(num)putchar(ch[num--]);
    putchar(
);return;
}
const int maxn=4002;
int n,m=0;
int b[maxn],num=0,minn=maxn,ru[maxn];
int lin[maxn],ver[maxn],nex[maxn],len=1;
int q[maxn<<1],t=0,vis[maxn];
void add(int x,int y)
{
    ver[++len]=y;
    nex[len]=lin[x];
    lin[x]=len;
}
void bfs()
{
    q[++t]=minn;
    while(t!=0)
    {
        int x=q[t],i=lin[x];
        while(i!=0&&vis[i]!=0)i=nex[i];//找到一条还未走过的边
        if(i!=0)//模拟了递归过程!
        {
            q[++t]=ver[i];//进队
            vis[i]=vis[i^1]=1;//成对变换len得为1
            lin[x]=nex[i];//删边
        }
        else t--,b[++num]=x;
    }
}
int main()
{
    //freopen("1.in","r",stdin);
    n=read();
    for(int i=1;i<=n;i++)
    {
        int x,y;
        x=read();y=read();
        add(x,y);add(y,x);
        m=max(m,max(x,y));
        minn=min(minn,min(x,y));
        ru[x]++;ru[y]++;
    }
    for(int i=1;i<=m;i++)if(ru[i]%2==1){minn=i;break;}
    bfs();
    for(int i=num;i>=1;i--)put(b[i]);
    return 0;
}
View Code

针对这道题求字典序用邻接表就不太好用了,所以建议直接使用邻接矩阵。

以上是关于欧拉路问题的主要内容,如果未能解决你的问题,请参考以下文章

欧拉路HDU3018

图的路径:欧拉路(欧拉回路)

hdu5883(欧拉路)

欧拉路和欧拉回路

欧拉路问题

欧拉路 / 回路 / 有向/ 无向 / 字典顺序