欧拉路径 DFS T3 1124. 骑马修栅栏 - AcWing题库 农民John每年有很多栅栏要修理。
他总是骑着马穿过每一个栅栏并修复它破损的地方。
John是一个与其他农民一样懒的人。
他讨厌骑马,因此从来不两次经过一个栅栏。
你必须编一个程序,读入栅栏网络的描述,并计算出一条修栅栏的路径,使每个栅栏都恰好被经过一次。
John能从任何一个顶点(即两个栅栏的交点)开始骑马,在任意一个顶点结束。
每一个栅栏连接两个顶点,顶点用 到 标号(虽然有的农场并没有 个顶点)。
一个顶点上可连接任意多( )个栅栏。
所有栅栏都是连通的(也就是你可以从任意一个栅栏到达另外的所有栅栏)。
你的程序必须输出骑马的路径(用路上依次经过的顶点号码表示)。
我们如果把输出的路径看成是一个500进制的数,那么当存在多组解的情况下,输出500进制表示法中最小的一个 (也就是输出第一个数较小的,如果还有多组解,输出第二个数较小的,等等)。
输入数据保证至少有一个解。
输入格式
第 行:一个整数 ,表示栅栏的数目;
第 到 行:每行两个整数 表示这条栅栏连接 与 号顶点。
输出格式
输出应当有 行,每行一个整数,依次表示路径经过的顶点号。
注意数据可能有多组解,但是只有上面题目要求的那一组解是认为正确的。
数据范围
,
输入样例:
9
1 2
2 3
3 4
4 2
4 5
2 5
5 6
5 7
4 6
输出样例:
1
2
3
4
2
5
4
6
5
7
思路
给一个无向图, 可以从任意一个非孤立点出发, 走到任何一个点上, 且经过所有的边。也就是欧拉路径问题。
先求出是否无解: 1)无向图, 只有当度数为奇数的点有0个或者2个时有解 2)有向图, 要么所有点的出度均等于入度;要么除了两个点之外,其余所有点的出度等于入度,剩余的两个点:一个满足出度比入度多1(起点),另一个满足入度比出度多1(终点).
但这里还要求是 500进制 最小的数, 也就是按字典序搜索。 根据欧拉回路搜索过程, 从一个点出发也一定会再回来, 且答案是逆序存储, 故假如从 点出发, 下一个点是 , 那么对应到答案就是 , 显然, 我们需要选择能走的最小的 。 若边数太多需要用邻接表时, 得排个序, 而这里点数比较小, 可以直接从小到大枚举。
代码流程:
- 读入边并统计出入度
- 找到起点start, 先定义为度数为0的点(0个时)
- 然后遍历所有点判断是否存在度数为奇数的点, 若有则为起点(2个时)
- 从起点进行DFS搜索
- 输出答案
代码
#include <iostream>
#include <cstring>
#include <algorithm>
#include <string>
#include <cmath>
using namespace std;
const int N = 550, M = 1026;
int g[N][N];
int d[N];
int n;
int res[M];
int cnt;
void dfs(int u)
{
for(int i = 1; i <= 500; i++)
{
if(g[u][i])
{
g[u][i]--, g[i][u]--;
dfs(i);
}
}
res[++cnt] = u;
}
int main()
{
cin >> n;
for(int i = 0; i < n;i ++)
{
int a,b;
cin >> a >> b;
g[a][b]++, g[b][a]++;
d[a]++, d[b]++;
}
int start = 1;
while(!d[start]) start++;
for(int i = 1; i <= 500; i++)
if(d[i] & 1)
{
start = i;
break;
}
dfs(start);
for(int i = cnt; i; i--) cout << res[i] << "\n";
return 0;
}