欧拉路径——精选推荐

合集下载
  1. 1、下载文档前请自行甄别文档内容的完整性,平台不提供额外的编辑、内容补充、找答案等附加服务。
  2. 2、"仅部分预览"的文档,不可在线预览部分如存在完整性等问题,可反馈申请退款(可完整预览的文档不适用该条件!)。
  3. 3、如文档侵犯您的权益,请联系客服反馈,我们会尽快为您处理(人工客服工作时间:9:00-18:30)。

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

定义:
图中经过所有边恰好⼀次的路径叫做欧拉路径。

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

判定:
判定当前图中是否存在欧拉路径其实⽐寻找更⿇烦
显然,欧拉回路也是欧拉路径,但为了⽅便区分,下⽂判定中的欧拉路径特指起点和终点不同。

判定⽅法:
⾸先,当且仅当这张图将有向边视为⽆向边时联通。

1. 有向图欧拉路径:图中恰好存在⼀个点(起点)出度⽐⼊度多1,恰好⼀个点(终点)⼊度⽐出度多1,剩下所有点⼊度等于出度。

2. 有向图欧拉回路:图中所有点⼊度等于出度(任⼀点都可以做起点和终点)。

3. ⽆向图欧拉路径:图中恰好有两个点的度数为奇数(起点和终点),其他所有点的度数为偶数。

4. ⽆向图欧拉回路:图中所有点的度数都为偶数(任⼀点都可以做起点和终点)。

寻找:
算法⼀:Fluery 算法。

时间复杂度O(m2),不常⽤。

算法⼆:Hierholzer 算法。

时间复杂度O(m),常⽤。

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

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

2. 当前点不存在出边时回退,并将当前点⼊栈P。

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

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

例题:
题⽬保证联通,所以直接判断⼊度和出度即可。

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

可以将边离线下来按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];
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;
}
练习:
将每个字母视为点,单词视为边,就和上⾯差不多了,注意欧拉路径的起始点。

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;
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;
}
变成了⽆向图,⼀样搞搞就过了。

#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;
}
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;
}
都差不多
这道题还挺有意思的,利⽤⼀个 trick,将⾏和列作为点,从⾏向列连边代表对应坐标的点,那么如果⼀个点有偶数条边就⼀半染红,⼀半染蓝,如果⼀个点有奇数条边就考虑连⼀个超源,因为多出来的边⽆论哪种颜⾊都可以。

分析⾏列间每条边会带来的影响,发现超源也⼀定是偶数条边,所以跑欧拉回路就好了,⼊边染⼀个颜⾊,出边染另⼀个颜⾊。

Processing math: 100%。

相关文档
最新文档