C++欧拉路径模板题

先放一些结论:

//欧拉路径:通过图中每条边恰好一次的路径。

//(起点与终点不同时的判据)

//有向图:起点出度比入度多 1,终点入度比出度多 1,其余点出入度相等。

//无向图:起点和终点是奇度数,其余点偶度数。

//欧拉回路:起点和终点相同的欧拉路径。

//有向图:所有点出入度相同。

//无向图:所有点偶度数。

这里主要讲一下欧拉路径,欧拉路径通俗来说就是一个图一笔画的轨迹,由上面的结论也知道,对于有向图来说,因为要一笔画,中间点肯定是有进有出故入度和出度相同,起点出发所以起点的入度少一,终点的出度少一。所以建图时顺便统计一下每个结点的入度和出度,当所有的结点入度和出度相同时,就说明可以随意从一个结点出发,最终能回到这个结点,任意一个结点都能够作为起点。

下面放一道洛谷的模板题,洛谷P7771【模板】欧拉路径

题目保证了图是连通的,要求输出字典序最小的欧拉路径。首先欧拉路径为了不漏环,所以我们需要在dfs时后序遍历,并倒叙输出(记住这个结论要这样做就行,具体证法去别的地方找找),所以我们需要用栈来记录后序遍历的点编号。并且因为要求字典序最小,我们存图后要对邻接表vector排序一下,让dfs时优先到达编号小的结点。然后在dfs的同时,删除已经访问过的边。

这里迎来了我当初一直卡的地方,也就是删边。我第一反应是用一个map存一下访问过的边,用结点代表出发点和到达点。然后一直过不去,后面控制变量一下我换了别人的删边方式就过了,发现因为这道题没有说保证不重边的情况,所以有可能出现反复横跳的情况,所以两个节点之间不一定只有一条边,故不能用map记录结点的方式来删边,下面放一下我的错误示范:

void dfs(int u){
    for(auto v:G[u]){
        if(vis[u][v]!=1) vis[u][v]=1,dfs(v);
    }
    sta[++top]=u;
}

那么应该如何正确删边呢?这里是用一个数组,记录每个结点应该要访问的下一个结点在邻接表里的下标,然后下次如果又路过这个点,就直接从这个数组对应的值下标访问邻接表,就可以起到删边的效果了,所有代码如下:

#include<bits/stdc++.h>
#define ll long long
#define pii pair<int,int>
const int maxn=100010,maxm=100010;//!注意,边和点的max值可能不一样
using namespace std;
vector<int>G[maxn];
int in[maxn],out[maxn];//记录结点的出入度
int sta[maxn],top=0;
map<int,map<int,bool>>vis;
int del[maxn];
//欧拉路径:通过图中每条边恰好一次的路径。
//(起点与终点不同时的判据)
//有向图:起点出度比入度多 1,终点入度比出度多 1,其余点出入度相等。
//无向图:起点和终点是奇度数,其余点偶度数。
//欧拉回路:起点和终点相同的欧拉路径。
//有向图:所有点出入度相同。
//无向图:所有点偶度数。
void dfs(int u){
    for(int i=del[u];i<G[u].size();i=del[u]){
        del[u]=i+1;
        dfs(G[u][i]);
    }
    sta[++top]=u;
}
int read(){
    int x=0,f=0,ch=getchar();
    while(!isdigit(ch)){if(ch=='-')f=1;ch=getchar();}
    while(isdigit(ch)){x=x*10+ch-'0';ch=getchar();}
    return f?-x:x;
}
int main(){
    int n=read(),m=read();
    for(int i=1;i<=m;i++){
        int u=read(),v=read();
        out[u]++;in[v]++;
        G[u].push_back(v);
    }
    for(int i=1;i<=n;i++)
        sort(G[i].begin(),G[i].end());
    int st=0,ed=0,flag=0;
    for(int i=1;i<=n;i++){
        if(in[i]==out[i]) continue;
        else if(in[i]==out[i]-1){
            if(!st) st=i;
            else flag=1;//已经有起点了
        }
        else if(in[i]-1==out[i]){
            if(!ed) ed=i;
            else flag=1;//已经有终点了
        }
        else flag=1;//出现了出度和入度相差超过2的结点
    }
    if(!st&&!ed) st=ed=1;
    else if(!st||!ed) flag=1;//没找到起点或者终点
    if(flag)//没有欧拉路径
        printf("No\n");
    else{
        dfs(st);
    for(int i=top;i;i--)
        printf("%d ",sta[i]);
    }
    system("pause");
    return 0;
}

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值