ICode9

精准搜索请尝试: 精确搜索
首页 > 其他分享> 文章详细

欧拉路径

2021-10-13 22:03:20  阅读:152  来源: 互联网

标签:head int 路径 dfs pn 欧拉


貌似很多博客都喜欢用一笔画来引入欧拉路径,但像您这样的强者时无需那些繁琐的东西,我们直接进入正题。


定义:

图中经过所有边恰好一次的路径叫做欧拉路径。

如果起点终点一样,那它就是欧拉回路。


判定:

判定当前图中是否存在欧拉路径其实比寻找更麻烦

显然,欧拉回路也是欧拉路径,但为了方便区分,下文判定中的欧拉路径特指起点和终点不同。

判定方法:

首先,当且仅当这张图将有向边视为无向边时联通。

  1. 有向图欧拉路径:图中恰好存在一个点(起点)出度比入度多1恰好一个点(终点)入度比出度多1,剩下所有点入度等于出度
  2. 有向图欧拉回路:图中所有点入度等于出度(任一点都可以做起点和终点)。
  3. 无向图欧拉路径:图中恰好有两个点的度数为奇数(起点和终点),其他所有点的度数为偶数
  4. 无向图欧拉回路:图中所有点的度数都为偶数(任一点都可以做起点和终点)。

寻找:

算法一:Fluery 算法。时间复杂度 \(O(m^2)\),不常用。

算法二:Hierholzer 算法。时间复杂度 \(O(m)\),常用。

只写 Hierholzer 算法,做法非常简单。

  1. 从起点开始 dfs,标记选了的边不能重复选,这里用类似Dinic的当前弧优化。

  2. 当前点不存在出边时回退,并将当前点入栈 \(P\)。

  3. dfs结束时倒序输出栈 \(P\) 中的节点即可。

算法导论上似乎有该算法证明。


例题:

P7771 【模板】欧拉路径

题目保证联通,所以直接判断入度和出度即可。

要求字典序最小,那么每次都要选能到达的最小的点。

可以将边离线下来按 \(v\) 从大到小排序,然后依次插入到链式前向星里,这样可以保证每次选到的都是最小的。

当前弧优化不加复杂度就假了。

code:

#include<bits/stdc++.h>
using namespace std;
#define int long long
#define in read()
inline int read(){
	int p=0,f=1;
	char c=getchar();
	while(c>'9'||c<'0'){if(c=='-')f=-1;c=getchar();}
	while(c>='0'&&c<='9'){p=p*10+c-'0';c=getchar();}
	return p*f;
}
const int N=1e5+5;
const int M=2e5+5;
int n,m;
struct edge{
	int v,nxt;
}e[M];
int head[N],en;
inline void insert(int u,int v){
	e[++en].v=v;
	e[en].nxt=head[u];
	head[u]=en;
}
struct QWQ{
	int u,v;
}E[M];
inline bool cmp(QWQ a,QWQ b){
	return a.v>b.v;
}
int p[M],pn;
void dfs(int u){
	for(int i=head[u];i;i=head[u]){
		int v=e[i].v;
		head[u]=e[i].nxt;
		dfs(v);
	}
	p[++pn]=u;
}
int flagin,flagout,flag;
int ind[N],outd[N];
int S=1;
signed main(){
	n=in,m=in;
	for(int i=1;i<=m;i++)
		E[i].u=in,E[i].v=in,
		outd[E[i].u]++,ind[E[i].v]++;
	sort(E+1,E+1+m,cmp);
	for(int i=1;i<=m;i++)
		insert(E[i].u,E[i].v);
	for(int i=1;i<=n;i++){
		if(ind[i]!=outd[i])flag++;
		if(ind[i]==outd[i]+1)flagout++;
		if(ind[i]+1==outd[i])flagin++,S=i;
	}
	if(flag==0||(flag==2&&flagout==1&&flagin==1)){
		dfs(S);
		for(int i=pn;i>=1;i--)
			cout<<p[i]<<' ';		
	}
	else cout<<"No";
	return 0;
}


练习:

P1127

将每个字母视为点,单词视为边,就和上面差不多了,注意欧拉路径的起始点。

code:

#include<bits/stdc++.h>
using namespace std;
#define int long long
const int N=1e3+5;
int n;
string s[N];
int len[N];
struct edge{
	int v,o,nxt;
}e[N];
int head[30],en;
inline void insert(int u,int v,int o){
	e[++en].v=v;
	e[en].o=o;
	e[en].nxt=head[u];
	head[u]=en;
}
struct QWQ{
	int u,v,o;
	string s;
}E[N];
bool cmp(QWQ a,QWQ b){
	return a.s>b.s;
}
int in[30],out[30];
int flagin,flagout,flag;
int S;
int mp[N][N];
int p[N],pn;
void dfs(int u){
	for(int i=head[u];i;i=head[u]){
		int v=e[i].v,o=e[i].o;
		head[u]=e[i].nxt;
		dfs(v);
		p[++pn]=o;
	}
}
signed main(){
	cin>>n;
	for(int i=1;i<=n;i++)
		cin>>s[i],len[i]=s[i].length(),
		E[i].u=s[i][0]-'a'+1,E[i].v=s[i][len[i]-1]-'a'+1,E[i].s=s[i],E[i].o=i,
		out[s[i][0]-'a'+1]++,in[s[i][len[i]-1]-'a'+1]++;		
	for(int i=1;i<=26;i++){
		if(out[i]!=in[i])flag++;
		if(out[i]==in[i]+1)flagout++,S=i;
		if(out[i]+1==in[i])flagin++;
	}
	if(!flag) for(int i=1;i<=26;i++)
			if(in[i]){S=i;break;}
	if(!flag||(flag==2&&flagout==1&&flagin==1)){
		sort(E+1,E+1+n,cmp);
		for(int i=1;i<=n;i++)
			insert(E[i].u,E[i].v,E[i].o);
		dfs(S);
		if(pn!=n)cout<<"***";
		else{
			for(int i=pn;i>=1;i--)
				cout<<s[p[i]]<<"."[i==1];
		}
	}
	else{
		cout<<"***";
	}
	return 0;
}


P2731

变成了无向图,一样搞搞就过了。

#include<bits/stdc++.h>
using namespace std;
#define int long long
#define in read()
inline int read(){
	int p=0,f=1;
	char c=getchar();
	while(c>'9'||c<'0'){if(c=='-')f=-1;c=getchar();}
	while(c>='0'&&c<='9'){p=p*10+c-'0';c=getchar();}
	return p*f;
}
const int N=505;
const int M=1100;
int m;
struct edge{
	int v,o,nxt;
}e[M<<1];
int head[N],en;
inline void insert(int u,int v,int o){
	e[++en].v=v;
	e[en].o=o;
	e[en].nxt=head[u];
	head[u]=en;
}
struct llmmkk{
	int u,v,o;
}E[M<<1];
inline bool cmp(llmmkk AK,llmmkk IOI){
	return AK.v>IOI.v;
}
int vis[M];
int d[N];
int p[M],pn;
void dfs(int u){
	for(int i=head[u];i;i=head[u]){
		int v,o=e[i].o;head[u]=e[i].nxt;
		while(vis[o]&&i){
			i=head[u],o=e[i].o,
			head[u]=e[i].nxt;			
		}
		if(!i)continue;
		v=e[i].v,vis[o]=1;
		dfs(v);
	}
	p[++pn]=u;
}
int n,minn,S;
signed main(){
	m=in;
	for(int i=1;i<=m;i++){
		E[i].u=in,E[i].v=in;
		E[i+m].u=E[i].v,E[i+m].v=E[i].u;
		E[i].o=E[i+m].o=i;
		d[E[i].u]++,d[E[i].v]++;
		n=max(n,max(E[i].u,E[i].v));
	}
	sort(E+1,E+1+(m<<1),cmp);
	for(int i=1;i<=(m<<1);i++)
		insert(E[i].u,E[i].v,E[i].o);	
	for(int i=n;i>=1;i--){
		if(d[i])minn=i;
		if(d[i]&1)S=i;
	}
	if(!S)S=minn;
	dfs(S);
	for(int i=pn;i>=1;i--)
		cout<<p[i]<<'\n';
	return 0;
}


P1341 都差不多


CF547D

这道题还挺有意思的,利用一个 trick,将行和列作为点,从行向列连边代表对应坐标的点,那么如果一个点有偶数条边就一半染红,一半染蓝,如果一个点有奇数条边就考虑连一个超源,因为多出来的边无论哪种颜色都可以。分析行列间每条边会带来的影响,发现超源也一定是偶数条边,所以跑欧拉回路就好了,入边染一个颜色,出边染另一个颜色。

标签:head,int,路径,dfs,pn,欧拉
来源: https://www.cnblogs.com/llmmkk/p/15404415.html

本站声明: 1. iCode9 技术分享网(下文简称本站)提供的所有内容,仅供技术学习、探讨和分享;
2. 关于本站的所有留言、评论、转载及引用,纯属内容发起人的个人观点,与本站观点和立场无关;
3. 关于本站的所有言论和文字,纯属内容发起人的个人观点,与本站观点和立场无关;
4. 本站文章均是网友提供,不完全保证技术分享内容的完整性、准确性、时效性、风险性和版权归属;如您发现该文章侵犯了您的权益,可联系我们第一时间进行删除;
5. 本站为非盈利性的个人网站,所有内容不会用来进行牟利,也不会利用任何形式的广告来间接获益,纯粹是为了广大技术爱好者提供技术内容和技术思想的分享性交流网站。

专注分享技术,共同学习,共同进步。侵权联系[81616952@qq.com]

Copyright (C)ICode9.com, All Rights Reserved.

ICode9版权所有