John's trip 【POJ - 1041】【欧拉回路】

本文深入探讨了欧拉回路的求解方法,对比了两种不同的深度优先搜索(DFS)策略,解释了为何在寻找欧拉回路时不能使用break语句,并通过实例说明了正确的方法是如何通过回溯来修正路径,最终找到字典序最小的欧拉回路。

题目链接


先说一下这道题的题意吧。

  输入到0 0结束,有一共N行输入,表示有N个街道,题目中保证了街道的标号一定是1~N且不会出现相同的街道。输入“x y z”表示x和y通过z号街道相链接(无向图)。现在,题目问的是,原图是否会形成一个欧拉回路,如果可以形成的话,输出的是所有欧拉回路中,字典序最小的(输入是输入边的序号,也就是“z”)。如果没有,则按题目输出,注意最后一个句号。

  关于这道题做过之后的感受呢,我这里的小小一个改动,让我去花了很多的时间理解欧拉回路,如果用的是dfs搜的话,可能会出现返回再回来重新搜索的这样的一个过程。

譬如说:这样的解法是正确的

void dfs(int u)
{
    for(int i=1, v; i<=M; i++)
    {
        if(!mp[u][i] || vis[i]) continue;   //vis用以剪枝,每条路径只用到过一次即可
        vis[i] = true;
        v = mp[u][i];
        mp[u][i] = mp[v][i] = 0;
        dfs(v);
        Stap[++Stop] = i;
    }
}

但是呢,我想,既然是欧拉回路,那么每条边都是只走一遍的,于是,我在这里擅自加上了break,会发生什么呢:

void dfs(int u)
{
    for(int i=1, v; i<=M; i++)
    {
        if(!mp[u][i] || vis[i]) continue;   //vis用以剪枝,每条路径只用到过一次即可
        vis[i] = true;
        v = mp[u][i];
        mp[u][i] = mp[v][i] = 0;
        dfs(v);
        Stap[++Stop] = i;
        break;
    }
}

很明显,就会WA掉了!

为什么呢?

这样真的可以保证每条边都是走一遍的吗?很显然, 我们通过举例一个欧拉通路来阐述这个问题:

  譬如说,我们现在的起点是3号点,我们如果要break的,我们按照dfs访问到的是“3--4--5--6--4”然后呢,就没有了!

  如果回到之前的正确的解法的时候呢,我们因为会回溯回来再重新跑(相当于是更正修复),所以dfs搜到的是“3--4--5--6--4--(回溯)--1--2--3”此时我们的栈会变成:从栈底往栈顶来看“4 6 5 4 3 2 1 3”。此时,就是正确的欧拉通路了。

  所以,这就是在告诉我们,当制造出桥的时候,我们更需要这样的回溯来进行修改,无论是欧拉图(欧拉回路)还是半欧拉图(欧拉通路)都是有这样的需求。

#include <iostream>
#include <cstdio>
#include <cmath>
#include <string>
#include <cstring>
#include <algorithm>
#include <limits>
#include <vector>
#include <stack>
#include <queue>
#include <set>
#include <map>
#include <bitset>
//#include <unordered_map>
//#include <unordered_set>
#define lowbit(x) ( x&(-x) )
#define pi 3.141592653589793
#define e 2.718281828459045
#define INF 0x3f3f3f3f
//#define INF 10000007.
#define eps 1e-7
#define HalF (l + r)>>1
#define lsn rt<<1
#define rsn rt<<1|1
#define Lson lsn, l, mid
#define Rson rsn, mid+1, r
#define QL Lson, ql, qr
#define QR Rson, ql, qr
#define myself rt, l, r
#define MP(a, b) make_pair(a, b)
using namespace std;
typedef unsigned long long ull;
typedef unsigned int uit;
typedef long long ll;
const int maxN = 55, maxM = 2e3 + 7;
int mp[maxN][maxM], du[maxN];
int N, M;
int Stap[maxM], Stop;
bool vis[maxM];
void dfs(int u)
{
    for(int i=1, v; i<=M; i++)
    {
        if(!mp[u][i] || vis[i]) continue;   //vis用以剪枝,每条路径只用到过一次即可
        vis[i] = true;
        v = mp[u][i];
        mp[u][i] = mp[v][i] = 0;
        dfs(v);
        Stap[++Stop] = i;
    }
}
int main()
{
    int x, y, z;
    while(scanf("%d%d", &x, &y) && (x | y))
    {
        scanf("%d", &z); N = max(x, y); M = z;
        for(int i=1; i<=50; i++) for(int j=1; j<=2000; j++) mp[i][j] = 0;
        for(int i=1; i<=50; i++) du[i] = 0;
        for(int i=1; i<=2000; i++) vis[i] = false;
        mp[x][z] = y; mp[y][z] = x; du[x]++; du[y]++;
        while(scanf("%d%d", &x, &y) && (x | y))
        {
            scanf("%d", &z);
            mp[x][z] = y; mp[y][z] = x; du[x]++; du[y]++;
            N = max(N, max(x, y)); M = max(M, z);
        }
        int cnt = 0;
        for(int i=1; i<=N; i++)
        {
            cnt += (du[i] & 1);
        }
        if(cnt) { printf("Round trip does not exist.\n"); continue; }
        Stop = 0;
        dfs(1);
        for(int i=Stop; i; i--) printf("%d%c", Stap[i], i == 1 ? '\n' : ' ');
    }
    return 0;
}

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Wuliwuliii

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值