【Ant Trip】题解

题目描述


给你无向图的n个点和m条边,保证这m条边都不同且不会存在同一点的自环边,现在问你至少要几笔才能所有边都画一遍。(一笔画的时候笔不离开纸)


输入格式
多组数据,每组数据用空行隔开。
对于每组数据,第一行两个整数n, m表示点数和边数。接下去m行每行两个整数a, b,表示a, b之间有一条边。


输出格式
对于每组数据,输出答案。


样例输入
3 3
1 2
2 3
1 3
4 2
1 2
3 4


样例输出


1
2


分析

做这道题,首先我们需要知道这些:

  • 若一张图只有一个点,那么一笔都不需要画
  • 假如他是一个半欧拉图,那么也只需要一笔
  • 以上两种都不是的话,那么我们需要画的笔数应该等于这张图中度为奇数的点数之和除以 2

那么我们怎么判断是否为一个欧拉图呢?暴搜?记得雷老师说过,假如一张图是欧拉图,那么他的所有点的度应该都是偶数。
其实这道题的题目并没有说所给出的数据是一张连通图,所以我们又可以用一个并查集来求出每一个联通分量,并用一个数组来存储这个联通分量之中的度为奇数的点的个数(好像有点啰嗦)


看懂了的话,可以自己实现一遍,发现有问题再看代码吧~

代码

#include <cstdio>
#include <cmath>
#include <cstring>
#include <iostream>
#include <algorithm>
using namespace std;

const int MAXN = 1e5 * 2 + 5;

int fa[MAXN];
int in[MAXN];
int num[MAXN];
int ans[MAXN];

int Find (int x) {
	if (fa[x] != x) fa[x] = Find (fa[x]);
	return fa[x];
}//找爸爸

int main() {
	int n, m;
	while ((scanf("%d %d", &n, &m)) != EOF) {
		for (int i = 1; i <= n; i++) fa[i] = i;
		memset (num, 0, sizeof num);
		memset (ans, 0, sizeof ans);
		memset (in, 0, sizeof in);//每次都要初始化数组(错了好几次,害)
		for (int i = 1; i <= m; i++) {
			int x, y;
			scanf("%d %d", &x, &y);
			in[x] ++;
			in[y] ++;//度累加
			x = Find (x);
			y = Find (y);
			if (x != y) fa[x] = y;
            //假如他们不在一个联通分量之中,就把他们放进同一个
		}
		for (int i = 1; i <= n; i++) {
			int x = Find (i); // i 的祖先。因为我们在之前是做了一个并查集的,假如说他们在一个连通分量之中,那么他们的祖先就应该是相同的
			num[x] ++;//这个联通分量中的点数累加
			if (in[i] % 2 == 1) ans[x] ++;//因为我们已经知道 i 是这个联通分量中的值了,所以在这里用 ans 数组来累加他所在的连通分量的度为奇数的点的个数
            //此处其实就是用祖先来作为下标方便储存,其实也可以在上面输入时合并 x 和 y 的时候就用一个 vector 数组来存储以 x 为祖先的一个集合,最后看那些集合是有数的,就做一遍下面的操作,请自己实现!
		} 
		int sum_ = 0;//用于累加答案
		for (int i = 1; i <= n; i++) {
			if (num[i] <= 1) continue;//假如只有 1 个点,就不用管(0 个更不用)
			if (ans[i] == 0) sum_ ++;//假如这个连通分量之中所有点的度都没有奇数,说明有欧拉路,一笔画成
			else sum_ += ans[i] / 2;//以上两种都不是的话,那么我们需要画的笔数应该等于这张图中度为奇数的点数之和除以 2
		} 
		printf("%d
", sum_);//完美结束
	}
	return 0;
}
原文地址:https://www.cnblogs.com/cqbzyanglin/p/13537844.html