原文链接:http://kakazai.cn/index.php/Kaka/Pat/query/id/216
题目链接:https://pintia.cn/problem-sets/994805342720868352/problems/994805349851185152
In graph theory, an Eulerian path is a path in a graph which visits every edge exactly once. Similarly, an Eulerian circuit is an Eulerian path which starts and ends on the same vertex. They were first discussed by Leonhard Euler while solving the famous Seven Bridges of Konigsberg problem in 1736. It has been proven that connected graphs with all vertices of even degree have an Eulerian circuit, and such graphs are called Eulerian. If there are exactly two vertices of odd degree, all Eulerian paths start at one of them and end at the other. A graph that has an Eulerian path but not an Eulerian circuit is called semi-Eulerian. (Cited from https://en.wikipedia.org/wiki/Eulerian_path)
Given an undirected graph, you are supposed to tell if it is Eulerian, semi-Eulerian, or non-Eulerian.
Input Specification:
Each input file contains one test case. Each case starts with a line containing 2 numbers N (≤ 500), and M, which are the total number of vertices, and the number of edges, respectively. Then M lines follow, each describes an edge by giving the two ends of the edge (the vertices are numbered from 1 to N).
Output Specification:
For each test case, first print in a line the degrees of the vertices in ascending order of their indices. Then in the next line print your conclusion about the graph – either Eulerian
, Semi-Eulerian
, or Non-Eulerian
. Note that all the numbers in the first line must be separated by exactly 1 space, and there must be no extra space at the beginning or the end of the line.
Sample Input 1:
7 12
5 7
1 2
1 3
2 3
2 4
3 4
5 2
7 6
6 3
4 5
6 4
5 6
Sample Output 1:
2 4 4 4 4 4 2
Eulerian
Sample Input 2:
6 10
1 2
1 3
2 3
2 4
3 4
5 2
6 3
4 5
6 4
5 6
Sample Output 2:
2 4 4 4 3 3
Semi-Eulerian
Sample Input 3:
5 8
1 2
2 5
5 4
4 1
1 3
3 2
3 4
5 3
Sample Output 3:
3 3 4 3 3
Non-Eulerian
在连通图中,若所有顶点的度数均为偶数,则该图为Eulerian;
在连通图中,若恰好有两个顶点的度数为奇数,则该图为Semi-Eulerian;
其他的都是Non-Eulerian。
所以,本题有两个关键点,1)图是否连通,2)各顶点的度数
在判断图的连通性时,可以使用并查集。若只有一个连通块,则图是连通图;否则,图是非连通图。
1)并查集
1)用python的话,最后一个测试点会超时
1 在接收每条边的同时,统计每个顶点的度数
2 用并查集判断图的连通性:一开始将每个顶点设置为独立的集合,然后不断合并有关系的顶点(这种关系指的是两个顶点之间有边相连)。如果最后合并剩一个集合,那么该图是连通的。如果剩下多个集合,该图不是连通的。
3 根据图的连通性和顶点的度数来判断它属于哪一种分类。
#include
#include
using namespace std;
/*数值范围*/
const int maxn = 501; //最多有500个顶点
/*所有变量*/
int father[maxn]; //并查集数组
int degree[maxn]; //存储每个点的度数
/* 并查集-初始化*/
void initiate(){
for(int i=0;i root;
int odd = 0;
for (int i = 1; i <= n; i++) {
root.insert(findroot(i));
if(degree[i]%2 == 1) odd++; //统计度为奇数的顶点个数
//打印点的度数
if(i!=1)printf(" ");
printf("%d",degree[i]);
if(i==n)printf("\n");
}
int num = root.size(); //根结点个数
/* 按要求输出 */
if (num == 1 && odd == 0)printf("Eulerian\n");
else if (num == 1 && odd == 2)printf("Semi-Eulerian\n");
else printf("Non-Eulerian\n");
return 0;
}
#!/usr/bin/python3
#code-python(3.6)
# 并查集-初始化
father = []
for i in range(501):
father.append(i)
#并查集-找根结点
def findroot(a):
while(father[a]!=a):
a = father[a]
return a
#并查集-合并集合+保证根结点最小
def union_ab(a,b):
fa = findroot(a)
fb = findroot(b)
father[fb] = fa
line = input().split(" ")
n = int(line[0])
m = int(line[1])
degree =[] #存好每个顶点的度数
for i in range(n+1): #初始化为0
degree.append(0)
#统计每个点的度,合并一条边的两个端点所在的集合
for i in range(m):
line = input().split(" ") #接受每行,并用空格分开
line = list(map(int,line)) #将字符串转为整数
id1,id2= line[0],line[1]
degree[id1] += 1
degree[id2] += 1
union_ab(id1,id2)
#遍历顶点,找出其所在集合的根结点,统计根结点个数
root = [] #存放根结点
odd = 0 #顶点的度为奇数的点的个数
for i in range(1,1+n):
root_i = findroot(i)
root.append(root_i)
if degree[i]%2==1:
odd += 1
root = set(root) #根结点去重
#按要求输出
degree = list(map(str,degree)) #degree转字符串
print(' '.join(degree[1:]))
if len(root)==1 and odd == 0:
print("Eulerian")
elif len(root) ==1 and odd == 2:
print("Semi-Eulerian")
else:
print("Non-Eulerian")