欧拉路题集

  • 有向图欧拉图:所有点的出度等于入度。
  • 有向图半欧拉路:起点的入度加一等于出度;终点的出度加一等于入读;其它点都是出度等于入度。
  • 无向图欧拉路:所有点的度数为偶数。
  • 无向图半欧拉路:起点和终点的读书为奇数,其它点为偶数。
  • 注意要判连通。并查集或者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;
}

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值