POJ 1300 判断是否存在欧拉回路(包含定理)

2024-06-08 23:58

本文主要是介绍POJ 1300 判断是否存在欧拉回路(包含定理),希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

判断存在欧拉回路的定理:

欧拉通路和欧拉回路的判定是很简单的,请看下面的定理及推论。
定理5.1 

无向图G 存在欧拉通路的充要条件是:
G 为连通图,并且G 仅有两个奇度结点(度数为奇数的顶点)或者无奇度结点。
推论5.1:
1) 当G 是仅有两个奇度结点的连通图时,G 的欧拉通路必以此两个结点为端点。
2) 当G 是无奇度结点的连通图时,G 必有欧拉回路。
3) G 为欧拉图(存在欧拉回路)的充分必要条件是G 为无奇度结点的连通图。

定理5.2 

有向图D 存在欧拉通路的充要条件是:
D 为有向图,D 的基图连通,并且所有顶点的出度与入度都相等;或者除两个顶点外,其余
顶点的出度与入度都相等,而这两个顶点中一个顶点的出度与入度之差为1,另一个顶点的出度
与入度之差为-1。
推论5.2:
1) 当D 除出、入度之差为1,-1 的两个顶点之外,其余顶点的出度与入度都相等时,D 的
有向欧拉通路必以出、入度之差为1 的顶点作为始点,以出、入度之差为-1 的顶点作为
终点。
2) 当D 的所有顶点的出、入度都相等时,D 中存在有向欧拉回路。
3) 有向图D 为有向欧拉图的充分必要条件是D 的基图为连通图,并且所有顶点的出、入度
都相等。


一:无向图的判定:

POJ 1300

题目描述:
你是一座大庄园的管家。庄园有很多房间,编号为0、1、2、3,...。你的主人是一个心不在
焉的人,经常沿着走廊随意地把房间的门打开。多年来,你掌握了一个诀窍:沿着一个通道,穿
过这些大房间,并把房门关上。你的问题是能否找到一条路径经过所有开着门的房间,并使得:
1) 通过门后立即把门关上;
2) 关上了的门不再打开;
3) 最后回到你自己的房间(房间0),并且所有的门都已经关闭了。
在本题中,给定房间列表,及连通房间的、开着的门,并给定一个起始房间,判断是否存在
这样的一条路径。不需要输出这样的路径,只需判断是否存在。假定任意两个房间之间都是连通
的(可能需要经过其他房间)。
输入描述:
输入文件包含多个(最多可达100 个)测试数据,每个测试数据之间没有空行隔开。
每个测试数据包括3 部分:
起始行-格式为“START M N”,其中M 为管理员起始所处的房间号,N 为房间的总数(1
≤N≤20);
房间列表-一共N 行,每行列出了一个房间通向其他房间的房间号(只需列出比它的号码大
的房间号,可能有多个,按升序排列),比如房间3 有门通向房间1、5、7,则房间3 的信息行内
容为“5 7”,第一行代表房间0,最后一行代表行间N-1。有可能有些行为空行,当然最后一行肯
定是空行,因为N-1 是最大的房间号;两个房间之间可能有多扇门连通。
终止行-内容为"END"。
输入文件最后一行是"ENDOFINPUT",表示输入结束。
输出描述:
每个测试数据对应一行输出,如果能找到一条路关闭所有的门,并且回到房间0,则输出"YES
X",X 是他关闭的门的总数,否则输出"NO"。


分析:
以房间为顶点、连接房间之间的门为边构造图。根据题目的意思,输入文件中每个测试数据
所构造的图都是连通的。本题实际上是判断一个图中是否存在欧拉回路或欧拉通路,要分两种情
况考虑:
1) 如果所有的房间都有偶数个门(通往其他房间),那么有欧拉回路,可以从0 号房间出发,
回到0 号房间。但是这种情况下,出发的房间必须为0,因为要求回到0 号房间。例如,
第1 个测试数据所对应的图为图5.6(a),图中有浅色阴影的顶点(即顶点0),表示管家
初始时所处的房间;在该测试数据中,管家可以回到0 号房间。
2) 有两个房间的门数为奇数,其余的都是偶数,如果出发的房间和0 号房间的门数都是奇
数,那么也可以从出发的房间到达0 号房间,并且满足题目要求。但是不能从房间0 出
发,必须从另一个门数为奇数的房间出发。例如第2、3 个测试数据就是这种情形,对应
的图为图(b)和图(c),输出的结果分别是"NO"和"YES 7"。
对于庄园的其他情形,都是不能完成题目中所要求的任务的,所以直接输出"NO"。
本题的难点在于输入数据的处理:可以使用sscanf函数处理数据。

#include <iostream>
#include <cstdio>
#include <fstream>
#include <algorithm>
#include <cmath>
#include <deque>
#include <vector>
#include <list>
#include <queue>
#include <string>
#include <cstring>
#include <map>
#define PI acos(-1.0)
#define mem(a,b) memset(a,b,sizeof(a))
#define sca(a) scanf("%d",&a)
#define pri(a) printf("%d\n",a)
#define M 202
#define INF 100000001
using namespace std;
typedef long long ll;
int main()
{char a[M];int m,n;while(cin>>a){if(strcmp(a,"ENDOFINPUT")==0) break;else cin>>m>>n;int door[101]={0},i,j,num=0,odd=0;getchar();for(i=0;i<n;i++){int k=0;gets(a);while(sscanf(a+k,"%d",&j)==1) //依次取出整形数据{num++;door[i]++; door[j]++;while(a[k]&&a[k]==' ') k++;while(a[k]&&a[k]!=' ') k++;}}cin>>a;for(i=0;i<n;i++){if(door[i]&1) odd++;  //看看有多少个点度数是奇点……}if(odd==0&&m==0) printf("YES %d\n",num); //如果没有奇点,而起始点是0的可以最后回到0点符合,如果第一个样例else if(odd==2&&door[m]&1&&door[0]&1&&m!=0)  //如果有2个奇点,且这两个起始点是0和另一个,并且起始点是另一个才能回到0点,如第三个样例printf("YES %d\n",num);else printf("NO\n");  //第二个样例}return 0;
}

二:有向图的判定

POJ 1386

题目描述:
有些秘门带有一个有趣的词迷。考古学家必须解开词迷才能打开门。由于没有其他方法可以
打开门,因此词迷就变得很重要。
每个门上有许多磁盘。每个盘上有一个单词,这些磁盘必须重新排列使得每个单词第一个字
母跟前一个单词最后一个字母相同。例如单词"acm"可以跟在单词"motorola"的后面。你的任务是
编写一个程序,读入一组单词,然后判定是否可以经过重组使得每个单词第一个字母跟前一个单
词最后一个字母相同,这样才能打开门。
输入描述:
输入文件中包含T 个测试数据。输入文件的第一行就是T,接下来是T 个测试数据。每个测
试数据的第一行是一个整数N,表示单词的个数(1≤N≤100000);接下来有N 行,每行是一个
单词;每个单词至少有2 个、至多有1000 个小写字母,即单词中只可能出现字母'a'~'z';在同一
个测试数据中,一个单词可能出现多次。
输出描述:
如果通过重组单词可以达到要求,输出"Ordering is possible.",否则输出"The door cannot be
opened."。


分析:
在本题中,每个单词只有首尾两个字母很关键,并且每个单词可以看成连接首尾两个字母的
一条有向边(由首字母指向尾字母)。这样每个测试数据中的一组单词可以构造成一个图:图中的
顶点为26 个小写字母,每个单词为图中的一条边。
构造好有向图后,题目要判定是否可以经过重组使得每个单词第一个字母跟前一个单词最后
一个字母相同,等效于判断图中是否存在一条路径经过每条边一次且仅一次,这就是有向欧拉通
路。本题的处理方法是:


(1) 读入每个单词时,因为每个单词相当于一条从首字母指向尾字母的边,所以对单词首字母
对应的顶点,出度加1;尾字母对应的顶点,入度加1;
(2) 26 个顶点的入度和出度都统计完毕后,根据各顶点的出度、入度关系来判断是否存在欧
拉通路,但要注意排除每个单词的首尾字母中没有出现过的字母。在下面的代码中,用bused[ ]
数组来表示每个字母是否在单词的首尾中出现。
(3) 判断完以后,还得判断整个有向图的基图(即不考虑边的方向)是否连通,如图5.7(b)
所示,每个顶点的出度、入度都相等,但这个图的基图是不连通的,所以也不存在有向欧拉通路。
判断连通最好的方法是使用并查集:考察图中所有的边(u, v),如果u 和v 不相同,且u 和v 不在
同一个连通分量上,则合并u 和v 各自所在的连通分量;

即利用并查集判断连通不连通!

#include <iostream>
#include <cstdio>
#include <fstream>
#include <algorithm>
#include <cmath>
#include <deque>
#include <vector>
#include <list>
#include <queue>
#include <string>
#include <cstring>
#include <map>
#define PI acos(-1.0)
#define mem(a,b) memset(a,b,sizeof(a))
#define sca(a) scanf("%d",&a)
#define pri(a) printf("%d\n",a)
#define M 100002
#define INF 100000001
using namespace std;
typedef long long ll;
int t,n,od[26],id[26],Exit[26],father[26],rank[26];
char str[1002];
struct edge
{int u,v;
}e[M];
int find(int x)
{return father[x]==x?x:father[x]=find(father[x]);
}
void uni(int u,int v)
{if(rank[u]>rank[v]) father[v]=u;else{if(rank[u]==rank[v]) rank[v]++;father[u]=v;}
}
int euler()
{int u,v,i,j=-1;for(i=0;i<26;i++){father[i]=i;  //并查集初始化rank[i]=0;}for(i=0;i<n;i++){u=find(e[i].u); v=find(e[i].v);if(u!=v) uni(u,v);}for(i=0;i<26;i++){if(Exit[i]==0) continue;if(j==-1) j=i;  //标记第一个exit[i]不为0的顶点else if(find(i)!=find(j)) break; //不连通}if(i<26) return 0; //不连通return 1;  //连通
}
int main()
{int u,v,i;sca(t);while(t--){mem(od,0);mem(id,0);mem(Exit,0);sca(n);for(i=0;i<n;i++){scanf("%s",str);u=str[0]-'a';v=str[strlen(str)-1]-'a';od[u]++; id[v]++;Exit[u]=Exit[v]=1;e[i].u=u; e[i].v=v;}int flag=1,od1=0,id1=0;for(i=0;i<26;i++){if(Exit[i]==0) continue;if(od[i]-id[i]>1||id[i]-od[i]>1) {flag=0;break;}  //因为只有一条路径连接所有的单词if(od[i]==0&&id[i]==0) {flag=0;break;}if(od[i]-id[i]==1){od1++;if(od1>1) {flag=0;break;}}if(id[i]-od[i]==1){id1++;if(id1>1) {flag=0;break;}}}if(od1!=id1) flag=0;else if(!euler()) flag=0;if(flag) printf("Ordering is possible.\n");else printf("The door cannot be opened.\n");}return 0;
}


这篇关于POJ 1300 判断是否存在欧拉回路(包含定理)的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/1043684

相关文章

poj 3974 and hdu 3068 最长回文串的O(n)解法(Manacher算法)

求一段字符串中的最长回文串。 因为数据量比较大,用原来的O(n^2)会爆。 小白上的O(n^2)解法代码:TLE啦~ #include<stdio.h>#include<string.h>const int Maxn = 1000000;char s[Maxn];int main(){char e[] = {"END"};while(scanf("%s", s) != EO

hdu 2602 and poj 3624(01背包)

01背包的模板题。 hdu2602代码: #include<stdio.h>#include<string.h>const int MaxN = 1001;int max(int a, int b){return a > b ? a : b;}int w[MaxN];int v[MaxN];int dp[MaxN];int main(){int T;int N, V;s

poj 1511 Invitation Cards(spfa最短路)

题意是给你点与点之间的距离,求来回到点1的最短路中的边权和。 因为边很大,不能用原来的dijkstra什么的,所以用spfa来做。并且注意要用long long int 来存储。 稍微改了一下学长的模板。 stack stl 实现代码: #include<stdio.h>#include<stack>using namespace std;const int M

poj 3259 uva 558 Wormholes(bellman最短路负权回路判断)

poj 3259: 题意:John的农场里n块地,m条路连接两块地,w个虫洞,虫洞是一条单向路,不但会把你传送到目的地,而且时间会倒退Ts。 任务是求你会不会在从某块地出发后又回来,看到了离开之前的自己。 判断树中是否存在负权回路就ok了。 bellman代码: #include<stdio.h>const int MaxN = 501;//农场数const int

poj 1258 Agri-Net(最小生成树模板代码)

感觉用这题来当模板更适合。 题意就是给你邻接矩阵求最小生成树啦。~ prim代码:效率很高。172k...0ms。 #include<stdio.h>#include<algorithm>using namespace std;const int MaxN = 101;const int INF = 0x3f3f3f3f;int g[MaxN][MaxN];int n

poj 1287 Networking(prim or kruscal最小生成树)

题意给你点与点间距离,求最小生成树。 注意点是,两点之间可能有不同的路,输入的时候选择最小的,和之前有道最短路WA的题目类似。 prim代码: #include<stdio.h>const int MaxN = 51;const int INF = 0x3f3f3f3f;int g[MaxN][MaxN];int P;int prim(){bool vis[MaxN];

poj 2349 Arctic Network uva 10369(prim or kruscal最小生成树)

题目很麻烦,因为不熟悉最小生成树的算法调试了好久。 感觉网上的题目解释都没说得很清楚,不适合新手。自己写一个。 题意:给你点的坐标,然后两点间可以有两种方式来通信:第一种是卫星通信,第二种是无线电通信。 卫星通信:任何两个有卫星频道的点间都可以直接建立连接,与点间的距离无关; 无线电通信:两个点之间的距离不能超过D,无线电收发器的功率越大,D越大,越昂贵。 计算无线电收发器D

poj 1502 MPI Maelstrom(单源最短路dijkstra)

题目真是长得头疼,好多生词,给跪。 没啥好说的,英语大水逼。 借助字典尝试翻译了一下,水逼直译求不喷 Description: BIT他们的超级计算机最近交货了。(定语秀了一堆词汇那就省略吧再见) Valentine McKee的研究顾问Jack Swigert,要她来测试一下这个系统。 Valentine告诉Swigert:“因为阿波罗是一个分布式共享内存的机器,所以它的内存访问

uva 10061 How many zero's and how many digits ?(不同进制阶乘末尾几个0)+poj 1401

题意是求在base进制下的 n!的结果有几位数,末尾有几个0。 想起刚开始的时候做的一道10进制下的n阶乘末尾有几个零,以及之前有做过的一道n阶乘的位数。 当时都是在10进制下的。 10进制下的做法是: 1. n阶位数:直接 lg(n!)就是得数的位数。 2. n阶末尾0的个数:由于2 * 5 将会在得数中以0的形式存在,所以计算2或者计算5,由于因子中出现5必然出现2,所以直接一

poj 3159 (spfa差分约束最短路) poj 1201

poj 3159: 题意: 每次给出b比a多不多于c个糖果,求n最多比1多多少个糖果。 解析: 差分约束。 这个博客讲差分约束讲的比较好: http://www.cnblogs.com/void/archive/2011/08/26/2153928.html 套个spfa。 代码: #include <iostream>#include <cstdio>#i