题目描述
给你无向图的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;
}