欧拉路和欧拉回路
一、基本概念:
欧拉路:欧拉路是指从图中任意一个点开始到图中任意一个点结束的路径,并且图中每条边通过的且只通过一次。
欧拉回路:欧拉回路是指起点和终点相同的欧拉路。
二、存在欧拉路的条件:
1.无向连通图存在欧拉路的条件:
所有点度都是偶数,或者恰好有两个点度是奇数,则有欧拉路。若有奇数点度,则奇数点度点一定是欧拉路的起点和终点,否则可取任意一点作为起点。
2.有向连通图存在欧拉路的条件:
- 每个点的入度等于出度,则存在欧拉回路(任意一点有度的点都可以作为起点)
- 除两点外,所有入度等于出度。这两点中一点的出度比入度大,另一点的出度比入度小,则存在欧拉路。取出度大者为起点,入度大者为终点。
三、算法实现:主要分为dfs和并查集两种方法,具体使用请看下面的题目
四、题目:
1.一笔画问题(NYOJ42)
描述
zyc从小就比较喜欢玩一些小游戏,其中就包括画一笔画,他想请你帮他写一个程序,判断一个图是否能够用一笔画下来。
规定,所有的边都只能画一次,不能重复画。
输入
第一行只有一个正整数N(N<=10)表示测试数据的组数。 每组测试数据的第一行有两个正整数P,Q(P<=1000,Q<=2000),分别表示这个画中有多少个顶点和多少条连线。(点的编号从1到P) 随后的Q行,每行有两个正整数A,B(0 < A,B < P),表示编号为A和B的两点之间有连线。
输出
如果存在符合条件的连线,则输出"Yes", 如果不存在符合条件的连线,输出"No"。
样例输入
2 4 3 1 2 1 3 1 4 4 5 1 2 2 3 1 3 1 4 3 4
样例输出
No
Yes
分析:这题是一题很经典的判断是否存在欧拉路的题目,我用了dfs和并查集两种方法写了这题,注意dfs要有适当的剪枝。下面请看两种ac代码:
dfs version:
#include<iostream> #include<vector> #include<cstring> using namespace std; const int maxn=1002; vector<int> graph[maxn]; int n,m,cnt,in; bool visited[maxn]; void dfs(int v) { for(int i=0;i<graph[v].size();i++) { int e=graph[v][i]; if(!visited[e]) { cnt++; if(graph[e].size()%2) in++; visited[e]=true; dfs(e); } } } int main() { int t; cin>>t; while (t--) { cin>>n>>m; for(int i=0;i<=n;i++) graph[i].clear(); for(int i=0;i<m;i++) { int x,y; cin>>x>>y; graph[x].push_back(y); graph[y].push_back(x); } cnt=0; in=0; memset(visited,false,sizeof(visited)); dfs(1); if((m==0&&n==1)||(cnt==n&&(in==0||in==2))) cout<<"Yes"<<endl; else cout<<"No"<<endl; } }
并查集方法
#include<iostream> #include<cstdio> #include<cstring> using namespace std; const int maxn=1005; int degree[maxn]; int fa[maxn]; void init() { for(int i=0;i<maxn;i++) fa[i]=i; } int find(int x) { if(x==fa[x]) return x; else { return fa[x]=find(fa[x]); } } void unite(int x,int y) { int rx=find(x); int ry=find(y); fa[ry]=rx; } int main() { int t; scanf("%d",&t); while(t--) { memset(degree,0,sizeof(degree)); init(); int n,m; scanf("%d%d",&n,&m); for(int i=1;i<=m;i++) { int x,y; scanf("%d%d",&x,&y); degree[x]++; degree[y]++; unite(x,y); } int root=find(1),cnt=0,d=0; for(int i=1;i<=n;i++) { if(find(i)!=root) cnt++; if(degree[i]%2) d++; } if(!cnt&&(d==0||d==2)) printf("Yes\n"); else printf("No\n"); } }
2.Ant trip (HDU 3018)
Ant Country consist of N towns.There are M roads connecting the towns.
Ant Tony,together with his friends,wants to go through every part of the country.
They intend to visit every road , and every road must be visited for exact one time.However,it may be a mission impossible for only one group of people.So they are trying to divide all the people into several groups,and each may start at different town.Now tony wants to know what is the least groups of ants that needs to form to achieve their goal.
Input
Input contains multiple cases.Test cases are separated by several blank lines. Each test case starts with two integer N(1<=N<=100000),M(0<=M<=200000),indicating that there are N towns and M roads in Ant Country.Followed by M lines,each line contains two integers a,b,(1<=a,b<=N) indicating that there is a road connecting town a and town b.No two roads will be the same,and there is no road connecting the same town.
Output
For each test case ,output the least groups that needs to form to achieve their goal.
Sample Input
3 3 1 2 2 3 1 3
Sample Output
1
2
Hint
New ~~~ Notice: if there are no road connecting one town ,tony may forget about the town. In sample 1,tony and his friends just form one group,they can start at either town 1,2,or 3. In sample 2,tony and his friends must form two group.
分析:
这题和上面的那一题稍微有点不同,这题求的是最少笔数。显然,这是属于欧拉回路的变形,首先要判断一共有多少个连通块,然后对每个连通块通过入度和出度判断笔数即可,即对于每个连通块的笔数:ceil(奇数度的点的个数/2.0),如果奇数度的点的个数为0,则笔数为1.
Accepted code:
#include<cstdio> #include<cstring> #include<map> #include<cmath> using namespace std; const int maxn=100005; int fa[maxn]; int degree[maxn]; int n,m; void init() { for(int i=0;i<=n;i++) fa[i]=i; memset(degree,0,sizeof(degree)); } int FindRoot(int x) { if(x==fa[x]) return x; else return fa[x]=FindRoot(fa[x]); } void unite(int x,int y) { int rx=FindRoot(x); int ry=FindRoot(y); if(rx!=ry) { fa[rx]=ry; } } int main() { while(~scanf("%d%d",&n,&m)) { init(); for(int i=1;i<=m;i++) { int x,y; scanf("%d%d",&x,&y); degree[x]++; degree[y]++; unite(x,y); } map<int,int> cnt; cnt.clear(); for(int i=1;i<=n;i++) { int ri=FindRoot(i); if(!cnt.count(ri)&°ree[ri]) cnt[ri]=0; if(degree[i]%2) cnt[ri]++; } int ans=0; for(map<int,int>::iterator it=cnt.begin();it!=cnt.end();it++) { if(it->second==0) { ans+=1; } else ans+=ceil(it->second/2.0); } printf("%d\n",ans); } }
由于时间原因,还有的题目分析我就不放上来了,只给出题目给大家练习吧。
HDU 1878 HDU 1116