- 有向图欧拉图:所有点的出度等于入度。
- 有向图半欧拉路:起点的入度加一等于出度;终点的出度加一等于入读;其它点都是出度等于入度。
- 无向图欧拉路:所有点的度数为偶数。
- 无向图半欧拉路:起点和终点的读书为奇数,其它点为偶数。
- 注意要判连通。并查集或者DFS
HDU1878
题解:无向图判欧拉路。模板题
代码:
#include <bits/stdc++.h>
using namespace std;
int const N = 1000 + 10;
int n,m;
int degree[N],fa[N];
bool euler(){
int odd = 0;
for(int i=1;i<=n;i++)
if(degree[i] & 1) odd++;
if(odd) return false;
else return true;
}
int find(int x){
return x == fa[x] ? x : (fa[x] = find(fa[x]));
}
void Union(int x,int y){
int fx = find(x), fy = find(y);
if(fx != fy) fa[fx] = fy;
}
bool connect(){
int root = 0;
for(int i=1;i<=n;i++){
if(find(i) == i) root++;
if(root >= 2) return false;
}
return true;
}
int main(){
while(cin>>n>>m && n){
for(int i=1;i<=n;i++) fa[i] = i;
memset(degree,0,sizeof(degree));
for(int i=1;i<=m;i++){
int x,y; cin>>x>>y;
Union(x,y);
degree[x]++, degree[y]++;
}
cout<<(connect() && euler())<<endl;
}
return 0;
}
POJ1300
题意:
- 输入m和n,从0到n-1,共有n个点。问从m出发能否经过所开着的门再回到0。
题解:无向图判欧拉图。
- 这一题输入比较复杂,我用stringstream还是0ms过。具体实现参考代码
- 这一题无向图求欧拉路。如果一开始就在0,那么就是求欧拉图。如果在其它点,就是求到0的半欧拉图。
- 这一题不用判环也能过。
代码:
#include <cstring>
#include <algorithm>
#include <vector>
#include <sstream>
#include <cstdio>
#include <iostream>
using namespace std;
int const N = 20 + 5;
string line;
int n,m,tmp,degree[N],cnt;
char s[1000];
bool Init(){
cnt = 0;
cin>>line;
if(line == "ENDOFINPUT") return false;
cin>>m>>n;
getchar(); //注意加
memset(degree,0,sizeof(degree));
for(int i=0;i<n;i++){
getline(cin,line);
stringstream ss(line);
while(ss>>tmp){
cnt++;
++degree[i], ++degree[tmp];
}
}
cin>>line;
return true;
}
bool solve(){
int odd = 0;
for(int i=0;i<n;i++)
if(degree[i] % 2) odd++; //统计奇数度点的个数
if(m == 0 && odd == 0) return true;
else if(m != 0 && odd == 2) return true;
return false;
}
int main(){
while(Init()){
if(!solve()) printf("NO\n");
else{
printf("YES %d\n",cnt);
}
}
return 0;
}
UVA10129
题解:
- 因为单词数量太多,拿单词做点会TLE。所以把每个单词的首末当作点,找欧拉路。
代码:
#include <bits/stdc++.h>
using namespace std;
int const N = 30;
int n;
string s;
int in[N],out[N],fa[N];
int find(int x){
return x == fa[x] ? x : (fa[x] = find(fa[x]));
}
void Union(int x,int y){
int fx = find(x), fy = find(y);
if(fx != fy) fa[fx] = fy;
}
void Init(){
for(int i=0;i<N;i++) fa[i] = i;
memset(in,0,sizeof(in));
memset(out,0,sizeof(out));
scanf("%d",&n);
for(int i=1;i<=n;i++){
cin>>s;
int x = s[0]-'a', y = s[s.length()-1] - 'a';
++out[x], ++in[y];
Union(x,y);
}
}
bool solve(){
int root = 0;
for(int i=0;i<26;i++){ //判断连通分量
if((in[i] || out[i]) && find(i) == i) root++;
if(root == 2) return false;
}
int st = 0,ed = 0;
for(int i=0;i<26;i++){
if(in[i] != out[i]){ //这个点肯定被用过
if(in[i] + 1 == out[i]) st++; //起点
else if(out[i] + 1 == in[i]) ed++; //终点
else return false; //不能够存在的点
}
}
if(st == ed && (st == 0 || st == 1)) return true;
else return false;
return true;
}
int main(){
int T;
scanf("%d",&T);
while(T--){
Init();
if(solve()) printf("Ordering is possible.\n");
else printf("The door cannot be opened.\n");
}
return 0;
}
POJ1041
题意:
- 从原点出发经过每条边一次,最后回到自己原点的地方。输出最短路线,且街道的字典序最小。
题解:
- 求无向图的欧拉回路,输出路径。
- 设mp[i][j] = k表示从i点出发经过j到达k。这样就好处理字典序。
代码:
#include <cstring>
#include <algorithm>
#include <cstdio>
#include <vector>
#include <iostream>
using namespace std;
int const N = 2000 + 10; //边的数量
int const M = 50 + 10; //点的数量
int x,y,z,st,n,m,cnt;
int degree[M];
int mp[M][N]; //mp[i][j] = k,表示点i经过j到达k
int ans[M],vis[N];
bool Init(){
n = m = 0;
memset(degree,0,sizeof(degree));
memset(mp,0,sizeof(mp));
cin>>x>>y;
if(!x && !y) return false;
cin>>z;
mp[x][z] = y, mp[y][z] = x;
++degree[x], ++degree[y];
st = min(x,y); n = max(n,max(x,y)); m++;
while(cin>>x>>y){
if(!x && !y) break;
n = max(n,max(x,y)); m++; //记录结点数目和边的数目
++degree[x], ++degree[y];
cin>>z;
mp[x][z] = y, mp[y][z] = x;
}
return true;
}
bool Judge(){ //判断度数要求
int odd = 0;
for(int i=1;i<=n;i++)
if(degree[i] & 1) odd++;
if(odd) return false;
else return true;
}
void dfs(int u){ //不需要回溯,因为必定存在欧拉回路,最后所有的边都遍历过了
for(int i=1;i<=m;i++){
if(mp[u][i] && !vis[i]){
vis[i] = true;
dfs(mp[u][i]);
ans[++cnt] = i;
}
}
}
void solve(){
cnt = 0;
memset(vis,0,sizeof(vis));
dfs(st);
for(int i=m;i>=1;i--) printf("%d ",ans[i]);
printf("\n");
}
int main(){
while(Init()){
if(!Judge()) printf("Round trip does not exist.\n");
else solve();
}
return 0;
}