zoukankan      html  css  js  c++  java
  • 欧拉路问题

    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

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

    下面是对欧拉路求出的题。使用上述方法不会超时。

    这道题显然是欧拉路和欧拉回路的例题,数据范围很大所以考虑用栈模拟dfs的过程下面是代码。

    #include<iostream>
    #include<cmath>
    #include<ctime>
    #include<cstdio>
    #include<iomanip>
    #include<cstring>
    #include<string>
    #include<stack>
    #include<algorithm>
    #include<map>
    #include<queue>
    #include<deque>
    #include<vector>
    #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/=10;
        while(num)putchar(ch[num--]);
        putchar('
    ');return;
    }
    const int maxn=50002;
    int n,m;
    int lin[maxn<<1],ver[maxn<<1],nex[maxn<<1],len=0;
    int q[maxn<<2],t=0,b[maxn<<1],h=0;
    void add(int x,int y)
    {
        ver[++len]=y;
        nex[len]=lin[x];
        lin[x]=len;
    }
    void bfs()
    {
        q[++t]=1;
        while(t!=0)
        {
            int x=q[t],i=lin[x];
            if(i!=0)lin[x]=nex[i],q[++t]=ver[i];
            else t--,b[++h]=x;
        }
    }
    int main()
    {
        //freopen("1.in","r",stdin);
        n=read();m=read();
        for(int i=1;i<=m;i++)
        {
            int x,y;
            x=read();y=read();
            add(x,y);add(y,x);
        }
        bfs();
        for(int i=h;i>=1;i--)put(b[i]);
        return 0;
    }
    View Code

    这里就会有疑问了对上述代码,会发现直接寻找下一条边,这样就可以满足当走完所有边时一定会回到起点么,这个答案肯定是肯定的。

    灵光一闪。如1连2,2连3,3连1,然后1走向2,2走向1,1走向3,3走向1,然后发现出栈了,这不是没有满足条件吗?注意尽管这样想是没有满足条件的别忘了上面可是大环套小环的,也就是说接下来是3到2,2到3,答案的储存是倒序的也就是dfs之后的b数组应该是这样的1(走到1走不动了。3(走到3走不动了,2,3,1,2,1.。这样看似上面的走错了,其实也是得到了正确答案,大环套小环,把小环嵌套进答案。最后怎么保证终点是起点呢?显然不和1相连的点都是小环,然后他们一定是在1的前面就被输出了,而1走向某个点肯定还有某个点最终走向1,双向图嘛,所以由此可得最后的点一定是起点(也就是1。(本人对上述代码的理解和对题目的理解)。

    就这样欧拉回路就被得到解决了,还要时常复习啊,然后就是概念得懂,最重要的就是dfs之后存答案实现嵌套作用!

    穷且益坚。

  • 相关阅读:
    交换机模拟器
    bootstrap-分页-默认分页
    bootstrap-分页-默认分页
    bootstrap-分页-默认分页
    交易系统查询带上for update
    集成开发环境(IDE) Mix介绍
    Dapp及相关开发工具介绍
    区块链技术视频网站EthCast.com上线
    区块链:最小可行区块链原理解析1
    账户、交易核心概念及投注合约解析
  • 原文地址:https://www.cnblogs.com/chdy/p/10099127.html
Copyright © 2011-2022 走看看