歐拉問題

歐拉回路

定義:

  • 給定一張圖,如何求出一條經過每條邊恰好一次的迴路。(行遍所有邊)
  • 必要條件:
  • 有向圖
    1.給定的圖是一個強連通分量(基圖是連通)。

基圖就是把有向邊變爲無向邊。

2.每個點的入度等於出度(進來了一定要出去)。
3.不難發現這兩個條件其實也是充分的。

  • 無向圖
    1.每個點的度數都是偶數(even)
    2.圖是連通的

圈套圈算法:

文字描述:

  • 任選一個起點,從起點開始dfsdfs,每條邊只能被走一遍,當沒有邊可以走的時候把xx壓入答案隊列中。
  • 最後的答案就是反着的歐拉回路。

解釋:

在這裏插入圖片描述

dfs(1)dfs(2)dfs(3)dfs(4)dfs(1)dfs(1)-dfs(2)-dfs(3)-dfs(4)-dfs(1),1無路可走,把1壓入答案隊列。
dfs(4)dfs(5)dfs(6)dfs(4)dfs(4)-dfs(5)-dfs(6)-dfs(4),4無路可走,把4壓入答案隊列。
依次求得:1 4 6 5 4 3 2 1
所以1 2 3 4 5 6 4 1位一條歐拉回路。

  • 原理:
    理想狀態下就是dfsdfs一個環沒有分支,當存在分支又是連通分量(前提是歐拉圖),那麼就可以跑完這個環再去遍歷下一個環,依次回溯。(符合fleuryfleury算法)
  • 複雜度:O(n)O(n)

代碼實現:

無向圖:

/*
無向圖的歐拉回路
鄰接矩陣存圖
*/
#include<cstdio>
#include<cstring>
const int N=1e3+10;
int ans[N];//存放點的順序
int book[N];//表示每個點的度數
int map[N][N];
int top,n,m;
void dfs(int x) {
	for(int i=1; i<=n; i++) {
		if(map[x][i]) {
			map[x][i]--;
			map[i][x]--;
			dfs(i);
		}
	}
	ans[top++]=x;//放入隊列中
}
void fleury(int x) {
	top=0;
	dfs(x);
}
void Print_road() { //輸出路徑
	for(int i=top-1; i>=0; i--) {
		printf("%d",ans[i]);
		if(i) {
			printf("-->");
		}
	}
	puts("");
}
int main() {
	int x;
	while(~scanf("%d %d",&n,&m)) { //共有n個點,m個邊   //歐拉回路所有邊只走一次
		memset(book,0,sizeof(book));
		memset(map,0,sizeof(map));
		for(int i=0; i<m; i++) {
			int u,v;
			scanf("%d %d",&u,&v);
			book[u]++;
			book[v]++;
			map[u][v]=map[v][u]=1;
		}//構建數組鏈表
		x=1;
		int cnt=0;
		for(int i=1; i<=n; i++) {
			if(book[i]&1) {
				cnt++;
				x=i;
			}
		}
		if(!cnt||cnt==2) { //找出x
			fleury(x);
			Print_road();
		} else {
			printf("不存在歐拉回路\n");
			continue;
		}
	}
}

有向圖:

/*
fleury 有向邊
用了鄰接表實現
*/

#include<cstdio>
#include<cstring>
const int N=1e3+10;
struct Node {
	int v;
	int next,index,flag;
} node[N];
int head[N];//存放每個u節點的第一個指向
int ans[N];//存放點的順序
int rbook[N],cbook[N];//表示每個點的度數
int top;
void dfs(int x) {
	int to;
	to=head[x];
	while(to!=-1) {
		if(node[to].flag) {
			node[to].flag=0;
			dfs(node[to].v);
		}
		to=node[to].next;
	}
	ans[top++]=x;
}
void fleury(int x) {
	top=0;
	dfs(x);
}
void Print_road() { //輸出路徑
	for(int i=top-1; i>=0; i--) {
		printf("%d",ans[i]);
		if(i)
			printf("-->");
	}
	puts("");
}
int main() {
	int n,m,x;
	while(~scanf("%d %d",&n,&m)) { //共有n個點,m個邊   //歐拉回路所有邊只走一次
		memset(rbook,0,sizeof(rbook));
		memset(cbook,0,sizeof(cbook));
		memset(head,-1,sizeof(head));

		for(int i=0; i<m; i++) {
			int u,v;
			scanf("%d %d",&u,&v);
			cbook[u]++;
			rbook[v]++;
			node[i].v=v;
			node[i].flag=1;
			node[i].index=i;
			node[i].next=head[u];
			head[u]=i;
		}//構建數組鏈表
		x=1;
		int cnt=0;
		for(int i=1; i<=n; i++) {
			if(rbook[i]!=cbook[i]) { //計算出度不等於入讀的個數
				cnt++;
				if(cbook[i]>rbook[i])//因爲總的出度與總的入度個數是相同的,所有必有一大一小
					x=i;
			}
		}
		if(!cnt||cnt==2) { //找出x
			fleury(x);
			Print_road();
		} else {
			printf("不存在歐拉回路\n");
			continue;
		}
	}
	return 0;
}

歐拉路徑

定義:

經過所有邊。相當於在歐拉回路里面去掉一條邊。

  • 必要條件:
    1.只有兩個度數爲奇數的點。
    2.連通。
發佈了305 篇原創文章 · 獲贊 222 · 訪問量 13萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章