欧拉路问题
Posted chdy
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了欧拉路问题相关的知识,希望对你有一定的参考价值。
euler路问题也称一笔画问题。
1.一张无向图,若存在一条从节点s到节点t的路径,恰好不重不漏地经过每条边一次(可以重复经过图中节点,最终回到节点s。
这条路径称该路径为s到t的euler回路。其实通过图中所有边的简单路就叫euler路。
2.特别的,如果存在一条从s出发的路径,恰好不重不漏地经过每条边一次最终回到s。
该条路径称为euler回路。其实就是闭合的欧拉路。
3.euler图:一张无向图,且无向图连通,每个点的度数都是偶数。其实就是包含euler回路的图。
4.euler路的判定:图中恰好有两个点度数为奇数,其他节点的度数为偶数。这两个度数为奇数的点就是起点与终点了。
5.euler回路的判定:图中所有点的度数都是偶数,任意点都是起点和终点。
dfs求出euler回路:
这道题保证是一个euler回路或者是euler路了,或者是euler回路加euler路,所以不需要再判断是否存在,直接求出字典序最小的答案即可。
首先是要看出起点应该在哪,发现如果有度数是奇数点的话起点就是最小的奇数点,而度数都是偶数点的话起点就在最小的偶数点。
数据范围小所以考虑直接邻接矩阵存储,然后字典序好得关键是答案输出的问题,回溯之后存答案,而不是回溯之前,为什么?
可以看出直接输出是不对的两个环套在一块,直接输出发现这个图连不起来了,所以回溯之后存。
究竟是什么原因:个人理解是由于如上图两环套在一起,一定要把第二个环完全嵌入第一个环之中,也就是说当你跑完第一个环时,第二个环还没跑,你要在跑完第一个环之前把第二个环给跑了才行。所以直接输出是不对滴~
#include<iostream> #include<cstring> #include<string> #include<ctime> #include<cmath> #include<iomanip> #include<algorithm> #include<queue> #include<vector> #include<stack> #include<cstdio> #include<map> #include<deque> #include<set> using namespace std; inline int read() { int x=0,f=1;char ch=getchar(); while(ch<‘0‘||ch>‘9‘){if(ch==‘-‘)f=-1;ch=getchar();} while(ch>=‘0‘&&ch<=‘9‘){x=x*10+ch-‘0‘;ch=getchar();} return x*f; } void put(int x) { if(x==0){putchar(‘0‘);putchar(‘ ‘);return;} if(x<0)x=-x,putchar(‘-‘); int num=0;char ch[50]; while(x)ch[++num]=x%10+‘0‘,x=x/10; while(num)putchar(ch[num--]); putchar(‘ ‘);return; } const int maxn=4002; int n,m=0; int a[maxn][maxn]; int b[maxn],len=0,minn=maxn,ru[maxn]; void dfs(int x) { for(int i=1;i<=m;i++) if(a[i][x]!=0) { a[i][x]--;a[x][i]--; dfs(i);b[++len]=i; } } int main() { //freopen("1.in","r",stdin); n=read(); for(int i=1;i<=n;i++) { int x,y; x=read();y=read(); a[x][y]++;a[y][x]++; m=max(m,max(x,y)); minn=min(minn,min(x,y)); ru[x]++;ru[y]++; } //cout<<minn<<endl; for(int i=1;i<=m;i++)if(ru[i]%2==1){minn=i;break;} dfs(minn); b[++len]=minn; for(int i=len;i>=1;i--)put(b[i]); return 0; }
复杂度是O(nm)的,其中n为点数,m为边数。复杂度有点高,况且当图很大是邻接矩阵存不下。
使用邻接表来存图,效率会更高。而且dfs递归乘数是m容易爆栈。再把dfs变成bfs形势的。
如果不要求字典序的话复杂度为O(n+m);
代码:
#include<iostream> #include<cstring> #include<string> #include<ctime> #include<cmath> #include<iomanip> #include<algorithm> #include<queue> #include<vector> #include<stack> #include<cstdio> #include<map> #include<deque> #include<set> using namespace std; inline int read() { int x=0,f=1;char ch=getchar(); while(ch<‘0‘||ch>‘9‘){if(ch==‘-‘)f=-1;ch=getchar();} while(ch>=‘0‘&&ch<=‘9‘){x=x*10+ch-‘0‘;ch=getchar();} return x*f; } void put(int x) { if(x==0){putchar(‘0‘);putchar(‘ ‘);return;} if(x<0)x=-x,putchar(‘-‘); int num=0;char ch[50]; while(x)ch[++num]=x%10+‘0‘,x=x/10; while(num)putchar(ch[num--]); putchar(‘ ‘);return; } const int maxn=4002; int n,m=0; int b[maxn],num=0,minn=maxn,ru[maxn]; int lin[maxn],ver[maxn],nex[maxn],len=1; int q[maxn<<1],t=0,vis[maxn]; void add(int x,int y) { ver[++len]=y; nex[len]=lin[x]; lin[x]=len; } void bfs() { q[++t]=minn; while(t!=0) { int x=q[t],i=lin[x]; while(i!=0&&vis[i]!=0)i=nex[i];//找到一条还未走过的边 if(i!=0)//模拟了递归过程! { q[++t]=ver[i];//进队 vis[i]=vis[i^1]=1;//成对变换len得为1 lin[x]=nex[i];//删边 } else t--,b[++num]=x; } } int main() { //freopen("1.in","r",stdin); n=read(); for(int i=1;i<=n;i++) { int x,y; x=read();y=read(); add(x,y);add(y,x); m=max(m,max(x,y)); minn=min(minn,min(x,y)); ru[x]++;ru[y]++; } for(int i=1;i<=m;i++)if(ru[i]%2==1){minn=i;break;} bfs(); for(int i=num;i>=1;i--)put(b[i]); return 0; }
针对这道题求字典序用邻接表就不太好用了,所以建议直接使用邻接矩阵。
以上是关于欧拉路问题的主要内容,如果未能解决你的问题,请参考以下文章