起源——七桥问题:城中有七座桥,每座桥连接两座岛屿,如何不重复地走遍7座桥。
欧拉转换成图问题:能否从无向图中的一个结点出发走出一条道路,每条边恰好经过一次,欧拉道路。一笔画问题。
在欧拉道路中,除了起点跟终点,其他点的度数应该是偶数,七桥问题中存在四个点度数是奇数(奇点)。
充分条件:如果一个无向图是连通的,且最多只有两个奇点,则一定存在欧拉道路。如果是两个奇点,一个起点,一个终点;如果不存在奇点,那么任意点出发,都会回到该点(欧拉回路)。
有向图的结论:最多只有两个点的入度不等于出度,而且必须是其中一个点的出度恰好比入度大一(起点),另一个点的入度比出度大一(终点)。
前提条件:忽略边的方向后,图必须是连通的。
欧拉通路: 通过图中每条边且只通过一次,并且经过每一顶点的通路。
欧拉回路: 通过图中每条边且只通过一次,并且经过每一顶点的回路。
无向图是否具有欧拉通路或回路的判定:
欧拉通路:图连通;图中只有0个或2个度为奇数的节点
欧拉回路:图连通;图中所有节点度均为偶数
有向图是否具有欧拉通路或回路的判定:
欧拉通路:图连通;除2个端点外其余节点入度=出度;1个端点入度比出度大1;一个端点入度比出度小1 或 所有节点入度等于出度
欧拉回路:图连通;所有节点入度等于出度
以下代码适用于欧拉道路和回路。
如果打印欧拉道路,主程序调用时,参数必须是道路的起点。打印顺序是逆序的,真正使用这份代码时,应把printf语句换成push语句,把边 (u,v)压入一个栈中。
void euler(int u){
for(int v=0;v<n;v++){
if(G[u][v] && !vis[u][v]){
vis[u][v] = vis[v][u] =1; //若为有向图 改为vis[u][v]=1;
euler(v);
printf("%d %d\n",u,v); //s.push(v)
}
}
}
以下例题参考:https://blog.csdn.net/u011499425/article/details/52685511
例题:单词(UVa 10129h)
输入n(n<=10000)个单词,是否可以把这些单词排成一个序列,使得每个单词的第一个字母和上一个单词的最后一个字母相同(例如:acm\malform\mouse)。每个单词最多包含1000个小写字母。输入中可以用重复单词
对于该题,可以把单词的首字母和尾字母看成顶点,一个单词,它的首尾字母存在一条有向边。这样,问题即为,每条边恰好经过一次。
因为字母作为顶点,题目里给的最多26个。 连通性的判断可以用bfs或dfs遍历即可,可以从某个入度或出度不为0(即26个字母中在该示例中出现了的字母)的顶点出发进行一次遍历,遍历结束后如果还有“出现了的字母”没被访问到,则说明不连通;或者也可以用dfs进行遍历所有的出现了的字母,然后计数,(相当于之前uva 572 求八连通块的个数)个数大于1则说明不连通。 需要注意,在一些地方,要判断26个字母每个字母是否出现。 在判断入度比出度大1、出度比入度大1后,不要忘了所有的点入度都等于出度也是可以的~
欧拉回路是图上的应用,具体我想了想,发现图有很多种情况,有些点可能自己连自己,图的情况可能会很复杂,具体没仔细去想,以后有机会就再学习学习。
#include<cstdio>
#include<cstring>
using namespace std;
int str[27][27],visit[27],du[27][2];//str为邻接矩阵,记录两个节点是否连通
//du用于记录字母的出度与入度
void read(){ //读取字符串,记录开头与结尾的字母
int n;
char por,tear,temp;
scanf("%d",&n);
getchar();
while(n--){
scanf("%c",&por);
du[por-'a'][1]++;
while(temp=getchar()!='\n'){
tear = temp;
}
du[tear-'a'][0]++;
str[por-'a'][tear-'a']=1;
}
}
void dfs(int s){
//深搜,找是否有通路
visit[s]=1;
for(int i=0;i<26;i++){
if(str[s][i]&&!visit[i]){
dfs(i);
}
}
}
int solve(){
int head=-1,tear =-1;
for(int i=0,count=0;i<26;i++){
if(du[i][0]!=du[i][1]){
if(du[i][0]-du[i][1]>=2 &&du[i][0]-du[i][1]<=-2){
return 0;
}else{
count++;
if(du[i][1]-du[i][0]==1){
head=i;
}else{
tear=i;
}
}
if(count>2) return 0;//如果出现多个出度与入度相差1的节点,则肯定不能有且只有一条通路
}
}
if(head==-1&&tear==-1){ //count=0 欧拉回路的情况
for(int i=0;i<26;i++){
if(du[i][0]){
dfs(i);
break;
}
}
}
else{
if(head==-1||tear==-1) return 0; //count=1
else{
dfs(head);//count = 2 从头开始搜
}
}
for(int i=0;i<26;i++){
if((du[i][0]+du[i][1])&&visit[i] || (!du[i][0]+du[i][1])&&!visit[i]);
//看其是否为通路(即有度的节点必须被访问到)
else return 0;
}
return 1;
}
int main(){
int t;
scanf("%d",&t);
while(t--){
memset(str,0,sizeof(str));
memset(visit,0,sizeof(visit));
memset(du,0,sizeof(du));
read();
if(solve()) puts("Ording is possible");
else {
puts("The door cannot be opened.");
}
}
return 0;
}
之前的欧拉(回)路板子
//不能解决多个回路的情况,只能找出一个回路 ,
//一笔画问题,不重复的走过所有的路径
#include<iostream>
#include<cstdio>
using namespace std;
int start,n,m,cnt,ol[101];
int du[101],g[101][101];
int read()
{
int r=0,k=1;
char c=getchar();
for(;c<'0'||c>'9';c=getchar())if(c=='-')k=-1;
for(;c>='0'&&c<='9';c=getchar())r=r*10+c-'0';
return r*k;
}
void find_ol(int i)
{
for(int j=1;j<=n;j++)
{
if(g[i][j]==1)
{
g[i][j]=g[j][i]=0;
find_ol(j);
}
}
ol[++cnt]=i;
return;
}
int main()
{
n=read();
m=read();
for(int i=1;i<=m;i++)
{
int x,y;
x=read();y=read();
g[x][y]=g[y][x]=1;
du[x]++;du[y]++;
}
start=1;
for(int i=1;i<=n;i++)
{
if(du[i]%2==1) start=i;//欧拉通路,不是欧拉回路,如果是回路,那么所有的点的度都是偶数
}
cnt=0;
find_ol(start);
for(int i=1;i<=cnt;i++)
printf("%d ",ol[i]);
cout<<endl;
return 0;
}