欧拉路径问题分为四种:
无向图的欧拉道路
无向图的欧拉回路
有向图的欧拉道路
有向图的欧拉回路
每一种的解决方式都是类似的
1》判断是否联通
2》判断是否满足有欧拉回路的条件
3》找出这条道路
对于有向图的连通问题可以将边看作是无向图,按照无向图来判断是否连通。
判断连通的方式有DFS和并查集,并查集以后再说,先来说说如何有DFS判断联通的问题
基本思路就是从一个点出发,如果可以一次dfs遍历到所有的点,那么就是连通的。
在这道题目中就是这样的。一次dfs后exist和vis数组相同
//先判断是否联通 #include<cstdio> #include<cstring> #include<vector> #include<iostream> using namespace std; const int maxn = 30; int G[maxn][maxn];//判断联通 int in_degree[maxn]; int out_degree[maxn]; int exist[maxn]; int vis[maxn]; void dfs(int x) { vis[x] = 1; for(int i = 0;i<26;i++) { if(!vis[i]&&G[x][i]) dfs(i); } } void print_vis_ex() { for(int i=0;i<26;i++) { printf("%d ",exist[i]); } printf("\n"); for(int i=0;i<26;i++) { printf("%d ",vis[i]); } printf("\n"); } bool connect() { for(int i = 0;i<26;i++) { if(!vis[i]&&exist[i]) { //printf("%d\n",i); dfs(i); break;//这个时候vis和exist应该是一样的,如果是联通的话 } } //print_vis_ex(); for(int i=0;i<26;i++) { if(vis[i]!=exist[i]) return false; } return true; } bool euler() {//入度全部等于出度,或者有一个入度大于出度1,另一个出度大于入度1 int cnt = 0,d; for(int pos = 0;pos < 26;pos++) { if(in_degree[pos]!=out_degree[pos]) { d = in_degree[pos]-out_degree[pos]; cnt++; } } if(cnt==0||(cnt==2&&(d==1||d==-1))) return true; return false; } void print_G() { for(int i= 0;i<26;i++) { for(int j = 0;j<26;j++) { printf("%d ",G[i][j]); } printf("\n"); } printf("\n\n"); } int main() { #ifdef local freopen("input.txt","r",stdin); freopen("out.txt","w",stdout); #endif int kase; scanf("%d",&kase); for(int i=0;i<kase;i++) { memset(in_degree,0,sizeof(in_degree)); memset(out_degree,0,sizeof(out_degree)); memset(G,0,sizeof(G)); memset(exist,0,sizeof(G)); memset(vis,0,sizeof(vis)); int x; scanf("%d",&x); for(int j=0;j<x;j++) { string s; cin>>s; out_degree[s[0]-'a']++; in_degree[s[s.size()-1]-'a']++; G[s[0]-'a'][s[s.size()-1]-'a']=1; G[s[s.size()-1]-'a'][s[0]-'a']=1; exist[s[0]-'a']=1; exist[s[s.size()-1]-'a']=1; } //print_G(); bool flag=false; if(connect()) { //printf("ccc\n"); if(euler()) flag = true; } if(flag) { printf("Ordering is possible.\n"); } else { printf("The door cannot be opened.\n"); } } return 0; }
判断完是否连通后,就需要去判断是否满足拥有欧拉路径的条件
无向图有欧拉道路的条件是,只有两个点的度数是奇数
无向图有欧拉回路的条件是,度数全部都是偶数
有向图有欧拉回路的条件是,每个点的出度和入度相等
有向图有欧拉道路的条件是,只有两个点的出度和入度不相同,而且一个是入度多1,一个是出度多1
在本题中,由于是有向图,那么只要它可以满足后两个条件中的任意一个就可以了。
判断思路是这样的,判断每个出度和入度,记录不相等的次数,和其中一次的差,如果不想等的次数是0,或者不相等的次数时2,而且其中一次是1或者-1(如果有一次是1,那么由于各点出度和入度的和一定是一样的,所有另一个一定是-1);
下面重点是如何寻找欧拉路径
void euler(int u) { for(int i= 0;i<n;i++) { if(G[u][i]&&vis[u][i]) { vis[u][i] = vis[i][u] = 1; euler(i); } } V.push_back(u); }
这段代码展示的就是寻找无向图的欧拉道路的情况,下面来解释一下:
想要完全理解这段代码,需要去理解DFS的运行机制,尤其是它是如何回溯返回的,分析就不说了,太麻烦;v中保存的就是欧拉路径中经过的点。
上面vis[][]需要解释一下,因为点可以重复访问,所以不能对点设置vis,必须对边设置vis。
当无向图变为有向图的时候,将上面的vis该为一个即可。dfs()的参数是起点。