题目描述
欧拉回路是指不令笔离开纸面,可画过图中每条边仅一次,且可以回到起点的一条回路。现给定一个图,问是否存在欧拉回路?
输入描述:
测试输入包含若干测试用例。每个测试用例的第1行给出两个正整数,分别是节点数N ( 1 < N < 1000 )和边数M;随后的M行对应M条边,每行给出一对正整数,分别是该条边直接连通的两个节点的编号(节点从1到N编号)。当N为0时输入结束。
输出描述:
每个测试用例的输出占一行,若欧拉回路存在则输出1,否则输出0。
示例1
输入
3 3 1 2 1 3 2 3 3 2 1 2 2 3 0
输出
1 0
题目链接:欧拉回路
参考资料:链接
最终AC代码:
#include <bits/stdc++.h> using namespace std; const int MAX=1005; int cnt[MAX], father[MAX]; int findFather(int x){ if(x != father[x]) father[x] = findFather(father[x]); return father[x]; } int main(){ int i, u, v, t, N, M; set<int> si; //记录出现的且非孤立的结点 set<int>::iterator it; while(cin >> N >> M){ for(i=0; i<N; i++){ cnt[i] = 0; father[i] = i; } si.clear(); for(i=0; i<M; i++){ scanf("%d %d", &u, &v); if(u == v) continue; si.insert(u); si.insert(v); cnt[u]++; //入度+1 cnt[v]++; u = findFather(u); //找父节点 v = findFather(v); if(u != v) father[v] = u; } t = 0; //0 表示存在欧拉回路 for(it=si.begin(); it!=si.end(); it++){ if(cnt[*it] & 1){ t = 1; //表示不存在欧拉回路 break; } } if(t){ //说明存在度数为奇数的点 printf("0 "); continue; } t = 0; //统计集合的个数 for(it=si.begin(); it!=si.end(); it++) if(*it == father[*it]) t++; if(t == 1) printf("1 "); else printf("0 "); } return 0; }
总结:题干虽然简洁,但是要解出这个题,还必须深刻理解题干,即:欧拉回路的要求是有且仅有一次经过所有边。这也就是说,要么图中出现的点都是孤立的点,要么出现的所有边只可以构成一个回路(存在多个回路还不行)。
刚开始想采用图中的遍历方式解决,后来写到一半写不下去,因为越写思路越复杂~然后参考了别人的思路,过了几天再自己写出来,我用的方法是:并查集+统计结点度数;具体思路过程如下:
1、如果结点u == v,或者在输入的边中没有出现某个结点,那么这样的点都可以视为孤立结点; 2、否则,用set(去重)记录出现的边所连接的两个结点值。并且,此时还要统计结点的度数,以及归并这两个集合。 3、输入完数据后,可以从条件“是否存在度数为奇数的结点”和“是否有且只存在一个集合(即一个回路)”判断。 4、这两个条件都必须检查,只有不存在度数为奇数的结点且只存在一个集合,才输出1;否则,输出0。
回看上述过程,这题看起来有点难的原因无非两个:一、是否可以将问题描述转化为用并查集+统计结点度数的方法,并得到对应的判断标准;二、是否可以想到用set来存储度数非0的结点(即排除孤立结点)。
如果可以把这两个问题想透彻,那么这个题就简单很多了。最后啰嗦几句,从最近刷的题来看,很多对于自己有难度的题都是难在这种问题的转化上。比如,遇到图的问题,可能最有效的解决方式并不是用图的相关算法,反而可能是采用并查集或结合其它算法才能解决。如果自己直接局限在图的算法中不能自拔,那么就可能很难顺利将题目解决了。