欧拉路问题(学习笔记)

欧拉路问题,俗称("一笔画"问题)

定义:给定一张无向图,若存在一条从\(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\n",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\n",ans[i]);
    return 0;
}

posted on 2019-09-23 22:22  PPXppx  阅读(315)  评论(0编辑  收藏  举报