图的遍历
1341:【例题】一笔画问题
【题目描述】
如果一个图存在一笔画,则一笔画的路径叫做欧拉路,如果最后又回到起点,那这个路径叫做欧拉回路。
根据一笔画的两个定理,如果寻找欧拉回路,对任意一个点执行深度优先遍历;找欧拉路,则对一个奇点执行dfs,时间复杂度为O(m+n),m为边数,n是点数。
【题目分析】
定理1:存在欧拉路的条件:图是连通的,有且只有2个奇点。
定理2:存在欧拉回路的条件:图是连通的,有0个奇点。
对每个点进行深搜,并对搜到的点进行计数count,如果count==n,则该路径为欧拉路 使用深搜可以记录路径并将路径倒着输出
【代码实现】
#include<bits/stdc++.h>
#define N 1005
using namespace std;
int f[N][N];
int s[N];
int b[N];
int n, m;
int t = 0;
void dfs(int z) {
for (int i = 1; i <= n; i++) {
if (f[z][i] == 1) {
f[z][i] = f[i][z] = 0;
dfs(i);
}
}
b[t++] = z;
}
int main() {
//定理1:存在欧拉路的条件:图是连通的,有且只有2个奇点。
//定理2:存在欧拉回路的条件:图是连通的,有0个奇点。
cin >> n >> m;
for (int i = 1; i <= m; i++) {
int x, y;
cin >> x >> y;
f[x][y] = f[y][x] = 1;
s[x]++;
s[y]++;
}
int flag = 1;
for (int i = 1; i <= n; i++)
if (s[i] % 2 == 1)
flag = i;
dfs(flag);
for (int i = 0; i < t; i++) {
cout << b[i] << " ";
}
return 0;
}
1374:铲雪车(snow)
【题目描述】
随着白天越来越短夜晚越来越长,我们不得不考虑铲雪问题了。整个城市所有的道路都是双车道,因为城市预算的削减,整个城市只有1辆铲雪车。铲雪车只能把它开过的地方(车道)的雪铲干净,无论哪儿有雪,铲雪车都得从停放的地方出发,游历整个城市的街道。现在的问题是:最少要花多少时间去铲掉所有道路上的雪呢?
【题目分析】
问题的本质:坐标点连接的欧拉回路
问题的求解:所有道路长度求和,并乘2得到总距离,根据速度求出时间,小时和分钟均为整数,分钟采用四舍五入
【代码实现】
#include <bits/stdc++.h>
using namespace std;
int main() {
long long int x, y;
// clock_t s = clock();
cin >> x >> y;
long long int x1, y1, x2, y2;
double sum = 0;
while (cin >> x1 >> y1 >> x2 >> y2) {
sum += sqrt((x2 - x1) * (x2 - x1) + (y2 - y1) * (y2 - y1));
}
double ans = sum * 2 / 20 / 1000;
long long int h = (int)ans;
long long int m = (int)((ans - h) * 60 + 0.5);
cout << h << ":" << setfill('0') << setw(2) << m;
// cout << endl << "the time cost is:" << clock() - s << endl;
return 0;
}
1375:骑马修栅栏(fence)
【题目描述】
农民John每年有很多栅栏要修理。他总是骑着马穿过每一个栅栏并修复它破损的地方。
John是一个与其他农民一样懒的人。他讨厌骑马,因此从来不两次经过一个一个栅栏。你必须编一个程序,读入栅栏网络的描述,并计算出一条修栅栏的路径,使每个栅栏都恰好被经过一次。John能从任何一个顶点(即两个栅栏的交点)开始骑马,在任意一个顶点结束。
每一个栅栏连接两个顶点,顶点用1到500标号(虽然有的农场并没有500个顶点)。一个顶点上可连接任意多(≥1)个栅栏。所有栅栏都是连通的(也就是你可以从任意一个栅栏到达另外的所有栅栏)。
你的程序必须输出骑马的路径(用路上依次经过的顶点号码表示)。我们如果把输出的路径看成是一个500进制的数,那么当存在多组解的情况下,输出500进制表示法中最小的一个 (也就是输出第一个数较小的,如果还有多组解,输出第二个数较小的,等等)。 输入数据保证至少有一个解。
【题目分析】
特别的:两个顶点可能连接多个栅栏,节点的编号信息不确定
数据存储:读入每一条边,使用邻接矩阵进行记录,两点的连接信息记录边的数量,统计最大节点的标号n
搜索准备:确定起点start,从1到n枚举每个点的度,找到奇数度的点,如果没有从1开始找存在连接关系的最小节点
深度搜索:对起点start进行深度优先搜索,并将搜索节点信息存入数组,最后反向打印节点信息
【代码实现】
#include <bits/stdc++.h>
using namespace std;
#define N 2005
int edge[N][N];
int n, m;
int du[N];
int pos, circle[N];
void dfs(int cur) {
circle[++pos] = cur;
for (int i = 1; i <= n; i++) {
if (edge[cur][i]) {
edge[cur][i]--;
edge[i][cur]--;
dfs(i);
// break;
}
}
}
int main() {
//input data
cin >> m;
for (int i = 1; i <= m; i++) {
int x, y;
cin >> x >> y;
edge[x][y]++;
edge[y][x]++;
du[x]++, du[y]++;
n = max(n, max(x, y));
}
// clock_t s = clock();
int start = 0;
for (int i = 1; i <= n; i++)
if (du[i] % 2 == 1) {
start = i;
break;
}
if (start == 0) {
for (int i = 1; i <= n; i++) {
if (du[i]) {
start = i;
break;
}
}
}
dfs(start);
for (int i = 1; i <= pos; i++) {
cout << circle[i] << endl;
}
//output time
// cout << endl << clock() - s << endl;
return 0;
}