欧拉路问题,俗称("一笔画"问题)
定义:给定一张无向图,若存在一条从(S)到(T)的路径,恰好不重不漏地经过每条边一次(可以重复经过图中的节点),则称该路径为(S)到(T)的欧拉路.
特别地,若存在一条从(S)出发的路径,恰好不重不漏地经过每条边一次(可以重复经过图中的节点),最终回到起点(S),则该路径为欧拉回路.
存在欧拉回路的无向图称作欧拉图.
欧拉图的判定:一张无向图为欧拉图,当且仅当无向图连通,并且每个点的度数都是偶数.
欧拉路的存在性判定:一张无向图中存在欧拉路,当且仅当无向图连通,并且图中恰好有两个节点的度数为奇数,其它节点的度数都是偶数.这两个度数为奇数的点就是欧拉路的起点(S)和终点(T).
inline void add(int a,int b){
nxt[++tot]=head[a];head[a]=tot;to[tot]=b;
}
inline void oula(){
st[++top]=1;//入栈
while(top>0){
int u=st[top],i=head[u];
while(i&&visit[i])i=nxt[i];//找到一条没有被访问过的边
if(i){
st[++top]=to[i];
visit[i]=visit[i^1]=1;
//标记为访问过,这里体现了tot=1的好处,每一条边被两个方向存储
//数组下标一定是2,3或者4,5这种异或1能够互相得到的
head[u]=nxt[i];
}
else --top,ans[++sum]=u;
//u相连的所有边均被访问过,则回溯.
}
}
int main(){
int n=read(),m=read();tot=1;
for(int i=1;i<=m;++i){
int a=read(),b=read();
add(a,b);add(b,a);
}
oula();
for(int i=sum;i>=1;--i)printf("%d
",ans[i]);//倒序输出
}
POJ,来一道模板题练练手.
题意:给定(N(N<=10000))个点(M(M<=50000))条边的无向图,求一条路径,从节点1出发,最后回到节点1,并且满足每条边恰好被正反两个方向分别经过一次.若有多种方案,输出任意一种即可.
分析:求欧拉回路的问题.在上述模板的基础上,去掉(visit)数组的标记即可保证每条边恰好被正反两个方向分别经过一次.
#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cstring>
#include<cmath>
#include<queue>
#include<map>
#include<set>
#define ll long long
using namespace std;
inline int read(){
int x=0,o=1;char ch=getchar();
while(ch!='-'&&(ch<'0'||ch>'9'))ch=getchar();
if(ch=='-')o=-1,ch=getchar();
while(ch>='0'&&ch<='9')x=x*10+ch-'0',ch=getchar();
return x*o;
}
const int N=100005;
int n,m;
int top,sum,st[N],ans[N];
int tot,head[N],nxt[N],to[N];
inline void add(int a,int b){
nxt[++tot]=head[a];head[a]=tot;to[tot]=b;
}
inline void oula(){
st[++top]=1;
while(top>0){
int u=st[top],i=head[u];
if(i){
st[++top]=to[i];
head[u]=nxt[i];
}
else --top,ans[++sum]=u;
}
}
int main(){
n=read();m=read();
for(int i=1;i<=m;++i){
int a=read(),b=read();
add(a,b);add(b,a);
}
oula();
for(int i=sum;i>=1;--i)printf("%d
",ans[i]);
return 0;
}