前言
本來以前老師講過“歐拉回路”,還專門放了個“七橋”的圖
結果時間太久我把它給忘了,摳腦殼想了半天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;
}