引入
哥尼斯堡(Koenigsberg)七桥问题 |
度娘好评😍:https://baike.baidu.com/item/七桥问题/2580504?fr=ge_ala
通俗一点就是一笔画问题,而我们把这样的问题称为:欧拉图问题
【欧拉图】区分一些概念:欧拉路径、欧拉回路、欧拉图
欧拉路径:给定一个连通图,若存在一条路径,经过图中每条边一次且仅一次(一笔画)
欧拉回路:在欧拉路径的基础上,需要最终回到起点
欧拉图:具有欧拉回路的图,称为欧拉图
图如果要存在欧拉路,就必须是所有边要连通的
在无向图下:
(1)欧拉路径的充要条件:奇点为0个或者是2个
(2)欧拉回路的充要条件:奇点为0个
有向图:
(1)欧拉路径的充要条件:要么所有点的出入度都相等;要么除了起点和终点外,所有点的出入度相等,起点的出度比入度多1,终点的入度比出度多1
(2)欧拉回路的充要条件:有点的出入度都相等
上面这些条件一定要记准确!!!
欧拉路
题目描述
有一个图,图中要么有两个奇点要么0奇点,如果是欧拉回路请从第一个点为起点开始遍历,如果有两个奇点,则以字典序小的为起点开始遍历,在遍历的过程中,字典序小的先遍历
输入描述
第一行两个整数,n和e,表示有n个节点,e条边,n<50.输出描述
只有一行,为欧拉路或欧拉回路。
样例
输入 5 5 1 2 2 3 3 4 4 5 5 1 输出
1 2 3 4 5 1
AC code~~~
#include<iostream>
#include<algorithm>
#include<cstring>
#include<cstdio>
#include<cmath>
#include<iomanip>
#include<stack>
#include<map>
#define TIE ios::sync_with_stdio(false),cin.tie(0),cout.tie(0)
#define N 110
#define INF 0x3f3f3f3f
using namespace std;
int a[N][N],du[N],ans[N];
int n,e,cnt,s=1;
void dfs(int x){
for(int i=1;i<=n;++i){
if(a[x][i]){
a[x][i]--,a[i][x]--;//走边,所以这里直接删边
dfs(i);
}
}
ans[++cnt]=x;
}
int main(){
TIE;cin>>n>>e;
for(int i=1;i<=e;++i){
int u,v;
cin>>u>>v;
a[u][v]++,a[v][u]++;
du[u]++,du[v]++;
}
for(int i=1;i<=n;++i){
if(du[i]&1){
s=i;
break;
}
}
dfs(s);
for(int i=cnt;i>=1;i--) cout<<ans[i]<<" ";//回溯时存的,所以倒序输出
return 0;
}
欧拉回路
题目描述 有一天一位灵魂画师画了一张图,现在要你找出欧拉回路,即在图中找一个环使得每条边都在环上出现恰好一次。 一共两个子任务:
- 这张图是无向图。
- 这张图是有向图。
输入描述 第一行一个整数 t,表示子任务编号t∈1,2,如果 t=1 则表示处理无向图的情况,如果t=2 则表示处理有向图的情况 第二行两个整数 n,m,表示图的结点数和边数 接下来 m 行中,第 i 行两个整数 ,vi,ui,表示第 i 条边(从 1开始编号)保证 1≤vi,ui≤n。
- 如果 t=1 则表示 vi 到 ui 有一条无向边。
- 如果 t=2 则表示 vi 到 ui 有一条有向边 图中可能有重边也可能有自环。
输出描述 如果不可以一笔画,输出一行 “NO”。
否则,输出一行 “YES”,接下来一行输出一组方案。
输入样例1
1
3 3
1 2
2 3
1 3
输出样例1
YES
3 -2 -1
输入样例2
2
5 6
2 3
2 5
3 4
1 2
4 2
5 1
输出样例2
YES
4 1 3 5 2 6
数据范围 全部数据: 1≤N≤105 0≤m≤2×105
AC code ~~~
#include<iostream>
#include<algorithm>
#include<cstring>
#include<cstdio>
#include<cmath>
#include<iomanip>
#include<stack>
#include<map>
#define TIE ios::sync_with_stdio(false),cin.tie(0),cout.tie(0)
#define N 100100
#define M 200100
#define INF 0x3f3f3f3f
using namespace std;
int t, n, m, cnt, k;
int head[N], to[M * 2], nxt[M * 2], idx = -1, ru[N], chu[N], ans[M * 2], vis[M * 2];
void add(int u, int v) {
to[++idx] = v, nxt[idx] = head[u], head[u] = idx;
}
void dfs(int x) { //因为链式前向星是从0下标开始记录边的,所以输入边的顺序=下标+1
for (int i = head[x]; ~i; i = nxt[i]) {
if (vis[i]) continue;
vis[i] = 1;
if (t == 1) vis[i ^ 1] = 1; //无向图奇数都是反向边(仔细思考为什么),i为偶数:i^1相当于变为+1的奇数;i为奇数变为-1的偶数
int tmp;
if (t == 1) {
tmp = (i >> 1) +1; //求这是输入的第几条边(为输出做准备)
if (i & 1) tmp = -tmp;//如果是第奇数次输入的,肯定是反向边
} else {
tmp = i + 1; //有向图
}
dfs(to[i]);
ans[++k] = tmp;
}
}
int main() {
TIE;
memset(head, -1, sizeof head);
cin >> t;
cin >> n >> m;
for (int i = 1; i <= m; ++i) {
int u, v;
cin >> u >> v;
add(u, v);
if (t == 1) add(v, u);
chu[u]++, ru[v]++;
}
if (t == 1) {
for (int i = 1; i <= n; ++i) {
if ((ru[i] + chu[i]) & 1) {
cout<<"NO"<<"\n";
return 0;
}
}
} else {
for (int i = 1; i <= n; ++i) {
if (ru[i] != chu[i]) {
cout<<"NO"<<"\n";
return 0;
}
}
}
for (int i = 1; i <= n; ++i) {
if (head[i] != -1) {
dfs(i);
break;
}
}
if (k < m) {
cout<<"NO"<<"\n";
return 0;
}
cout<<"YES"<<"\n";
for (int i = k; i >= 1; --i) cout << ans[i] << " ";
return 0;
}
简单并查集+欧拉路
蚂蚁之旅
蚂蚁国由N个城镇组成,有M条道路连接。
托尼和他的朋友们打算走遍每一条道路,每条路都必须要走一次。然后,如果一群人只在一起是可能不能完成的,他们试图把人分成几组,每组都可以从不同的城镇开始,现在托尼想知道为了实现目标需要最少分出多少组?
输入描述
多组输入,每组之间以换行分割
对于每组数据:
第一行输入两个整数N,M,含义如题所示
然后输入M行,每行包含两个整数a,b,表示有一条道路连接城镇a和城镇b
蚂蚁国崇尚节俭,两个城市之间最多只有一条直接道路,也不会有道路连接是同一个城镇
输出描述
对于每个数据,输出答案,答案占一行。
输入样例
3 3
1 2
2 3
1 3
4 2
1 2
3 4
输出样例
1
2
提示
第一组数据图形如下所示:
因此只需要一组即可
第二组数据图形如下所示:
这个图中包含两个连通的区域,因此需要两组。
数据范围
全部数据:
1≤N≤105,0≤M≤2×105
冷静分析:
这个题就是让我们求有多少个连通块,并查集再好不过了。我们在输入的时候把每个通过边连接的点都并到一块,选出代表元;然后通过记录奇点来判断有多少个欧拉路径,欧拉回路需要单独判断(通过题意可知,如果是孤立点的话就不用管它,因为它没有边呀)
AC code~~~
#include<iostream>
#include<algorithm>
#include<cstring>
#include<cstdio>
#include<cmath>
#include<iomanip>
#include<stack>
#include<map>
#define TIE ios::sync_with_stdio(false),cin.tie(0),cout.tie(0)
#define N 200010
#define INF 0x3f3f3f3f
using namespace std;
int n,m,a,b,du[N],vis[N],father[N];
int Find(int x){
if(x==father[x]) return x;
return father[x]=Find(father[x]);
}
void combine(int x,int y){
x=Find(x);
y=Find(y);
father[x]=y;
}
int main(){
while(scanf("%d%d",&n,&m)!=EOF){
memset(du,0,sizeof du);
memset(vis,0,sizeof vis);
for(int i=1;i<=n;++i) father[i]=i;
for(int i=1;i<=m;++i){
cin>>a>>b;
du[a]++,du[b]++;
combine(a,b);
}
int num=0;
for(int i=1;i<=n;++i){
if(du[i]&1){
num++;
vis[Find(i)]=1;
}
}
num/=2;
for(int i=1;i<=n;++i){
if(du[i]){
if(!vis[Find(i)]&&Find(i)==i) num++;
}
}
printf("%d\n",num);
}
return 0;
}
完结撒花~~~(这个颜色是真的好看)