【歐拉 回/通 路(板題)】USA3.3——騎馬修柵欄 Riding the Fences

前言

本來以前老師講過“歐拉回路”,還專門放了個“七橋”的圖

結果時間太久我把它給忘了,摳腦殼想了半天QAQ...後來才發現原來是個板...

關於歐拉回/通路的good博客:

https://www.cnblogs.com/adelalove/p/8497000.html

https://blog.csdn.net/qq_41730082/article/details/84927199

題目

題目背景

Farmer John每年有很多柵欄要修理。他總是騎着馬穿過每一個柵欄並修復它破損的地方。

題目描述

John是一個與其他農民一樣懶的人。他討厭騎馬,因此從來不兩次經過一個柵欄。你必須編一個程序,讀入柵欄網絡的描述,並計算出一條修柵欄的路徑,使每個柵欄都恰好被經過一次。John能從任何一個頂點(即兩個柵欄的交點)開始騎馬,在任意一個頂點結束。

每一個柵欄連接兩個頂點,頂點用1到500標號(雖然有的農場並沒有500個頂點)。一個頂點上可連接任意多(>=1)個柵欄。兩頂點間可能有多個柵欄。所有柵欄都是連通的(也就是你可以從任意一個柵欄到達另外的所有柵欄)。

你的程序必須輸出騎馬的路徑(用路上依次經過的頂點號碼錶示)。我們如果把輸出的路徑看成是一個500進制的數,那麼當存在多組解的情況下,輸出500進製表示法中最小的一個 (也就是輸出第一位較小的,如果還有多組解,輸出第二位較小的,等等)。

輸入數據保證至少有一個解。

輸入格式

第1行: 一個整數F(1 <= F <= 1024),表示柵欄的數目

第2到F+1行: 每行兩個整數i, j(1 <= i,j <= 500)表示這條柵欄連接i與j號頂點。

輸出格式

輸出應當有F+1行,每行一個整數,依次表示路徑經過的頂點號。注意數據可能有多組解,但是隻有上面題目要求的那一組解是認爲正確的。

輸入輸出樣例

輸入

9
1 2
2 3
3 4
4 2
4 5
2 5
5 6
5 7
4 6

輸出

1
2
3
4
2
5
4
6
5
7

說明/提示

題目翻譯來自NOCOW。

USACO Training Section 3.3

題目大意

給你一個無向連通圖,求字典序最小的歐拉 回/通 路節點編號

分析

(一)首先我們要知道無向圖關於歐拉路的知識:

定理

 無向圖G存在歐拉通路的充要條件是:G爲連通圖,並且G僅有兩個奇度結點(度數爲奇數的頂點)或者無奇度結點。

推論

(1) 當G是僅有兩個奇度結點的連通圖時,G的歐拉通路必以此兩個結點爲端點;

(2)當G是無奇度結點的連通圖時,G必有歐拉回路

(3)G爲歐拉圖(存在歐拉回路)的充分必要條件是  G爲無奇度結點的連通圖


(二)選擇起點

由於本題是無向連通圖且保證有解,於是只有兩種情況:

1.圖爲歐拉圖(存在歐拉回路):無奇點

2.圖爲半歐拉圖(存在歐拉通路):有奇點

所以只用從小到大檢查是否有奇點,如果有就從該點開始,沒有就從1開始(保證字典序最小)


(三)如何找歐拉 回/通 路

DFS搜索

利用歐拉定理判斷出一個圖存在歐拉通路或歐拉回路後,選擇一個正確的起始頂點,用DFS算法遍歷所有的邊(每條邊只遍歷一次),遇到走不通就回退。

在搜索前進方向上將遍歷過的邊按順序記錄下來。這組邊的排列就組成了一條歐拉通路或迴路。

似乎還有一種方法,只不過我沒怎麼學,簡單貼一下:

(Fleury)佛羅萊算法

設G爲一個無向歐拉圖,求G中一條歐拉回路的算法如下:

(1) 任取G中一頂點v0,令P0=v0;

(2)假設沿Pi=v0e1v1e2v2......eivi走到頂點vi,按下面方法從E(G)-{e1,e2,...,ei}中選ei+1。

        ei+1與vi相關聯

        除非無別的邊可供選擇,否則ei+1不應該是Gi=G-{e1,e2,...,ei}中的橋。

(3)當(2)不能再進行時算法停止。

        可以證明的是,當算法停止時,所得到的簡單迴路Pm=v0e1v1e2v2......emvm,(vm=v0)爲G中一條歐拉回路。

DFS代碼

/*
ID:lunasmi2
TASK:fence
LANG:C++                 
*/
#include<cstdio>
#include<cmath>
#include<cstring>
#include<iostream>
#include<algorithm>
using namespace std;
const int MAXN=1000;
int a[MAXN+5][MAXN+5],ind[MAXN+5],ans[MAXN+5];
int m,cnt,S,k;
void dfs(int u)
{
	for(int i=1;i<=500;i++)
		if(a[u][i])
		{
			a[u][i]--;a[i][u]--;
			dfs(i);
		}
	ans[++k]=u;
}
int main()
{
    //freopen("fence.in","r",stdin);
    //freopen("fence.out","w",stdout);
    scanf("%d",&m);
    for(int i=1;i<=m;i++)
    {
    	int u,v;
    	scanf("%d%d",&u,&v);
    	a[u][v]++;a[v][u]++;
    	ind[u]++;ind[v]++;
	}
	//因爲題目保證有解,所以只用看有沒有奇點,沒有就從最小的1開始 
	S=1;
	for(int i=1;i<=500;i++)
		if(ind[i]&1)//是奇點 
		{
			S=i;
			break;
		}
	dfs(S);
	for(int i=k;i>=1;i--)
		printf("%d\n",ans[i]);
	return 0;
}

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章