题目链接
先说一下这道题的题意吧。
输入到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;
}

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

466

被折叠的 条评论
为什么被折叠?



