定义:
欧拉路径:在一个图中,由i点出发,将每个边遍历一次最终到达j点的一条路径。
欧拉回路:i=j时的欧拉路径。
欧拉回路与路径的判断方法:
1、在无向图中:
欧拉回路:每个点的度为偶数。
欧拉路径:仅 i 与 j 两点的度为奇数,其余点为偶数。
2、在有向图中:
欧拉回路:每个点的入度等于出度。
欧拉路径:仅 i 点的出度比入度多 1 , j 点的的入度比出度多 1 。
在无向图中的代码实现:
#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<vector>
#include<stack>
using namespace std;
int G[100][100], n, du[100], ans[10000], temp, len;
void dfs(int k)
{
// ans[len++] = k;
//有人可能说加在这里不更好吗,最后还不用倒着输出,直接正着标记路径
//这样是不可以的,因为这样每次进入一个点他都会直接先保存了。
//放到最后,保险,因为最后存储,正好存的都是运行过的,保留的现场的值
//而放到开头,在for、循环里会引起,当dfs回溯之后,如果发现新的再次进去的时候
//放到开头的它只会不停的记录,而放到最后的话,是都运行完,记录运行完毕现场的值/
for(int i = 1; i <= 100; i++){
if(G[k][i]){
G[k][i] = 0;
G[i][k] = 0;
dfs(i);
}
}
ans[len++] = k; //保存路径
}
int main()
{
cin>>n;
for(int i = 1; i <= n; i++){
int x, y;
scanf("%d %d", &x, &y);
du[x]++; //x的度数++
du[y]++;
G[x][y] = 1; //建图
G[y][x] = 1;
}
for(int i = 1; i <= 100; i++){
if(du[i] % 2 == 1) //如果说度数为奇数
temp++;
}
if(temp != 0 && temp != 2) printf("既不是欧拉回路也不是欧拉路径\n");
if(temp == 0){ //是欧拉回路,所有的度数都为偶数
for(int i = 1; i <= 100; i++){
if(du[i] != 0){ //随便找到一个点
dfs(i);
break;
}
}
}
if(temp == 2){ //是欧拉路径, 因为正好就有两个度数为奇数的点
for(int i = 1; i <= 100; i++){
if(du[i] % 2 == 1){ //因为,欧拉路径的起点应该是度数为奇数的点,所以遍历找到一个度数为奇数的点
dfs(i);
break;
}
}
}
for(int i = len - 1; i >= 1; i--){
printf("%d", ans[i]);
}
return 0;
}
在有向图中的代码实现:
#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<vector>
#include<stack>
using namespace std;
struct node
{
int to; //出度
int from; //入度
}du[100];
int G[100][100], n, ans[10000], temp, len, a, b;
void dfs(int k) //
{
for(int i = 1; i <= 100; i++){
if(G[k][i]){
G[k][i] = 0;
dfs(i);
}
}
ans[len++] = k; //保存路径
}
int main()
{
cin>>n;
for(int i = 1; i <= n; i++){
int x, y;
scanf("%d %d", &x, &y); //从x指向y
du[x].to++; //x的出度+1
du[y].from++; //y的入度+1
G[x][y] = 1; //建图,从x指向y
}
for(int i = 1; i <= 100; i++){
if(du[i].to != du[i].from) temp++; //入度数不等与出度数
else if(du[i].to - du[i].from == 1) a++; //出度比入度多1
else if(du[i].from - du[i].to == 1) b++; //入度比出度多1
}
if(temp != 0 && temp != 2) printf("既不是欧拉回路也不是欧拉路径\n");
if(temp == 0){ //是欧拉回路,所有的入度都等于出度
for(int i = 1; i <= 100; i++){
if(du[i].to != 0){ //随便找到一个点
dfs(i);
break;
}
}
}
if(temp == 2 && a == 1 && b == 1){ //是欧拉路径, 因为仅 i 点的出度比入度多 1 , j 点的的入度比出度多 1 。
for(int i = 1; i <= 100; i++){
if(du[i].to - du[i].from == 1){ //因为,欧拉路径的起点应该是出度比入度多 1
dfs(i);
break;
}
}
}
for(int i = len - 1; i >= 1; i--){
printf("%d", ans[i]);
}
return 0;
}
注释解释还算详细,如有错误欢迎指正。
附加一个联系题:https://www.luogu.org/problemnew/show/P1341
题解:
其实就是一个欧拉路问题,给你的两个字母对,你可以转化为int型,其实就是图中两个点,又因为题目说这俩字母没有顺序,所以是无向图,又说字典序最小,其实就直解遍历的时候从最小的点开始就行,又因为使得每个字母对都在这个字符串中出现,所以其实就是欧拉路的一笔画问题。
AC代码:
#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cstring>
using namespace std;
int n, vis[100], G[100][100], du[100], ans[10000], temp, len = 0;
void dfs(int k)
{
for(int i = 0; i <= 90; i++){
if(G[k][i]){
G[k][i] = 0;
G[i][k] = 0;
dfs(i);
}
}
ans[len] = k;
len++;
}
int main()
{
cin>>n;
memset(vis, 0, sizeof(vis));
memset(du, 0, sizeof(du));
memset(G, 0, sizeof(G));
for(int i = 1; i <= n; i++){
char a, b;
int a_int, b_int;
cin>>a>>b;
a_int = a - 'A';
b_int = b - 'A';
du[a_int]++;
du[b_int]++;
G[a_int][b_int] = 1;
G[b_int][a_int] = 1;
}
for(int i = 0; i <= 90; i++) if(du[i] % 2 == 1) temp++;
if(temp != 0 && temp != 2){printf("No Solution"); return 0;}
if(temp == 0){ //欧拉回路
for(int i = 0; i <= 90; i++){
if(du[i] != 0){
dfs(i);
break;
}
}
}
if(temp == 2){
int i;
for(i = 0; i <= 90; i++){
if(du[i] % 2 == 1)
break;
}
dfs(i);
}
for(int i = len - 1; i >= 0; i--) printf("%c", ans[i] + 'A');
return 0;
}