欧拉路径
欧拉路径
欧拉路径
定义:可以一笔画走完且不重复经过一条边的路径
可用欧拉路径走完有向连通图判定:
- 所有点入度与出度之差 \(\le1\)
- 入度与出度之差为1的点个数为0或2(总度数为偶数,这种点不可能只有1个,若有2个则一起点一终点)
可用欧拉路径走完无向连通图判定:
度数为奇数的点的个数为0或2(不可能只1个,理由同上,若有2个则一起点一终点)
插播道题—— 【一本通提高篇欧拉回路】单词游戏
题意:若一个单词的第一个字母与另一个单词最后一个字母相同,则这两个词可接龙,每个词只能用一次,问是否能把给出的所有词接龙成功。
这道题不难,关键在于如何建图
接龙只用看首尾,中间没用
我们可以将每个词看作从首字母连向尾字母的一条有向边,看整个图是否有欧拉路径走完
判定如上,就不放代码了
那么问题来了,一个无向连通图中有几条极长欧拉路径呢?(互不共用边)
也就是这个图最少几笔能画完?
有下列情况:
- 有一条欧拉路径走完,判定见上
- 设此图有n个度数为奇数的点(\(n\) 为偶数且 \(n \not=\) \(0\) 或 \(2\)),则有 \(\frac{n}{2}\) 条欧拉路径
若图不连通就划分为几个连通图
如何找一条欧拉路径?
我们发现,从路径起点开始遍历图的每条边,回溯后将当前点加入path数组中,最后倒序输出即可
代码如下:
void dfs(int x)
{
for(int i = minn; i <= maxn; i++)
if(g[x][i])
{
g[x][i]--, g[i][x]--; // 邻接矩阵存图,把走过边删掉
dfs(i);
}
path[++cnt] = x;
}
for(int i = minn; i <= maxn; i++)
if(du[i] % 2)
{
dfs(i);
flag = 1;
break; // 无欧拉回路
}
if(!flag) dfs(minn);
for(int i = cnt; i > 1; i--) printf("%d\n", path[i]);
#include<bits/stdc++.h>
#define reg register
using namespace std;
const int N = 400010;
int n, m, u, v, ru[N], st, ed, chu[N], lin[N], cnt, head[N];
vector<int> edge[N];
inline void dfs(int x)
{
// cerr << x << " ";
for(reg int i = head[x]; i < (int)edge[x].size(); i = head[x])
{ // 注意,i = head[x],否则会跑满 m^2,时间复杂度不对
head[x] = i + 1, dfs(edge[x][i]);
}
lin[++cnt] = x;
}
int main()
{
scanf("%d%d", &n, &m);
for(reg int i = 1; i <= m; ++i)
{
scanf("%d%d", &u, &v);
edge[u].push_back(v), ++ru[v], ++chu[u];
}
for(reg int i = 1; i <= n; ++i)
if(chu[i] != ru[i])
{
if(st && ed) {printf("No"); return 0;}
if(!st) st = i;
else if(!ed) ed = i;
}
if(st && !ed) {printf("No"); return 0;}
if(!st) st = 1;
for(reg int i = 1; i <= n; ++i) sort(edge[i].begin(), edge[i].end());
dfs(st);
for(reg int i = cnt; i > 0; --i) printf("%d ", lin[i]);
return 0;
}
欧拉回路
定义:一条起点终点相同的欧拉路径
可用欧拉路径走完有向连通图判定:
- 所有点入度与出度之差<=1
- 入度与出度之差为1的点个数为0
可用欧拉路径走完无向连通图判定:
度数为奇数的点的个数为0
求路径同欧拉路径,若有字典序要求则把点/边排序
看道题—— 【一本通提高篇欧拉回路】欧拉回路
思路则是求欧拉回路,不过输出可能会麻烦一些,要用边的号码判断反向或正向
代码如下——
#include<bits/stdc++.h>
using namespace std;
int t, n, m, v, u, du[100010], ru[100010], book[400010], path[100010], cnt;
vector<pair<int, int> > edge[100010];
int cmp(const pair<int, int> &a, const pair<int, int> &b)
{
return a.second < b.second;
}
void dfs(int x)
{
sort(edge[x].begin(), edge[x].end(), cmp); // 按字典序输出
for(int i = 0; i < edge[x].size(); i++)
{
if(!book[edge[x][i].second])
{
book[edge[x][i].second] = 1;
if(t == 1)
{
if(edge[x][i].second <= m) book[edge[x][i].second + m] = 1;
else book[edge[x][i].second - m] = 1;
} //双向边两条都得去掉
dfs(edge[x][i].first);
if(t == 1) path[++cnt] = edge[x][i].second > m ? (edge[x][i].second - m) * -1 : edge[x][i].second; // 判断方向
else path[++cnt] = edge[x][i].second;
}
}
}
int main()
{
scanf("%d%d%d", &t, &n, &m);
for(int i = 1; i <= m; i++)
{
scanf("%d%d", &v, &u);
edge[v].push_back(make_pair(u, i));
if(t == 1) edge[u].push_back(make_pair(v, i + m)), du[u]++, du[v]++;
else ru[u]++, du[v]++;
}
if(t == 1)
{
for(int i = 1; i <= n; i++)
if(du[i] % 2)
{
printf("NO");
return 0;
}
}
else if(t == 2)
{
for(int i = 1; i <= n; i++)
if(ru[i] != du[i])
{
printf("NO");
return 0;
}
}
// 判断有无欧拉回路
for(int i = 1; i <= n; i++)
if(du[i])
{
dfs(i);
break;
} // 找字典序最小点开始
if(cnt != m)
{
printf("NO");
return 0;
}
else
{
printf("YES\n");
for(int i = cnt; i > 0; i--)
{
printf("%d ", path[i]);
} // 反向输出
}
return 0;
}