一本通 3.4.1 图的遍历

图的遍历

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;
}
  • 2
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值