题目地址:
https://www.acwing.com/problem/content/1126/
农民John每年有很多栅栏要修理。他总是骑着马穿过每一个栅栏并修复它破损的地方。John是一个与其他农民一样懒的人。他讨厌骑马,因此从来不两次经过一个栅栏。你必须编一个程序,读入栅栏网络的描述,并计算出一条修栅栏的路径,使每个栅栏都恰好被经过一次。John能从任何一个顶点(即两个栅栏的交点)开始骑马,在任意一个顶点结束。每一个栅栏连接两个顶点,顶点用 1 1 1到 500 500 500标号(虽然有的农场并没有 500 500 500个顶点)。一个顶点上可连接任意多( ≥ 1 ≥1 ≥1)个栅栏。所有栅栏都是连通的(也就是你可以从任意一个栅栏到达另外的所有栅栏)。你的程序必须输出骑马的路径(用路上依次经过的顶点号码表示)。我们如果把输出的路径看成是一个 500 500 500进制的数,那么当存在多组解的情况下,输出 500 500 500进制表示法中最小的一个(也就是输出第一个数较小的,如果还有多组解,输出第二个数较小的,等等)。输入数据保证至少有一个解。
输入格式:
第 1 1 1行:一个整数 F F F,表示栅栏的数目;第 2 2 2到 F + 1 F+1 F+1行:每行两个整数 i , j i,j i,j表示这条栅栏连接 i i i与 j j j号顶点。
输出格式:
输出应当有 F + 1 F+1 F+1行,每行一个整数,依次表示路径经过的顶点号。注意数据可能有多组解,但是只有上面题目要求的那一组解是认为正确的。
数据范围:
1 ≤ F ≤ 1024 1≤F≤1024 1≤F≤1024
1 ≤ i , j ≤ 500 1≤i,j≤500 1≤i,j≤500
相当于求一个欧拉路径,具体算法参考https://blog.csdn.net/qq_46105170/article/details/115060171。题目要求输出字典序最小的路径,只需在DFS的时候,每次都选取编号尽量小的顶点的那条边去走,最后将路径反序即可。注意DFS的时候仍然要做后序遍历,即回溯之前再将顶点加入路径。
有一个细节需要注意。出发点的选取方面,首先一定要选有邻边的点,在此情况下,再找度是奇数的顶点(如果原图不存在欧拉回路,但存在欧拉路,那么出发点和终点是唯二的度为奇数的顶点,选择其一为出发点即可),如果找不到度为奇数的点,那就从任意点出发都可以。
代码如下:
#include <iostream>
using namespace std;
const int N = 510, M = 1050;
int n, m;
// 可以用邻接矩阵存图,这样可以方便的按照从小到大的顺序遍历邻边,
// 其中g[i][j]表示从i到j的边有多少个
int g[N][N];
int res[M], cnt;
// d存每个顶点的度
int d[N];
void dfs(int u) {
// 从小到大遍历邻接点
for (int i = 1; i <= n; i++)
if (g[u][i]) {
g[u][i]--, g[i][u]--;
dfs(i);
}
res[++cnt] = u;
}
int main() {
cin >> m;
for (int i = 0; i < m; i++) {
int a, b;
cin >> a >> b;
g[a][b]++, g[b][a]++;
d[a]++, d[b]++;
n = max(n, max(a, b));
}
int s = 1;
// 先找到非孤立点
while (!d[s]) s++;
// 再找度为奇数的点,如果找到了,则赋给s;如果找不到,那么s也可以作为DFS的起点
for (int i = 1; i <= n; i++)
if (d[i] % 2) {
s = i;
break;
}
dfs(s);
// 逆序输出路径
for (int i = cnt; i; i--) cout << res[i] << endl;
return 0;
}
时间复杂度 O ( F ) O(F) O(F),空间 O ( n 2 + F ) O(n^2+F) O(n2+F), n n n是顶点个数。