定義
通過圖中所有邊恰好一次且行遍所有頂點的通路稱為歐拉通路。
通過圖中所有邊恰好一次且行遍所有頂點的回路稱為歐拉回路。
具有歐拉回路的無向圖或有向圖稱為歐拉圖。
具有歐拉通路但不具有歐拉回路的無向圖或有向圖稱為半歐拉圖。
有向圖也可以有類似的定義。
非形式化地講,歐拉圖就是從任意一個點開始都可以一筆畫完整個圖,半歐拉圖必須從某個點開始才能一筆畫完整個圖。
性質
歐拉圖中所有頂點的度數都是偶數。
若 \(G\) 是歐拉圖,則它為若干個環的並,且每條邊被包含在奇數個環內。
判別法
對於無向圖 \(G\) ,
\(G\) 是歐拉圖的充要條件是 \(G\) 聯通,所有點的度數為偶數 ;
\(G\) 是半歐拉圖的充要條件是 \(G\) 聯通,只可能有 \(0/2\) 個點的度數為奇數 .
對於有向圖 \(G\) ,
\(G\) 是歐拉圖的充要條件是 \(G\) 聯通,所有點處於同一個強連通分塊里面,每個點的入度和出度相同 .
\(G\) 是半歐拉圖的充要條件是
- 將 \(G\) 中的所有邊視為無向邊,此時所有點處於同一個聯通塊中 .
- 最多存在一個頂點 \(d^+(u)-d^-(u)=1\)
- 最多存在一個頂點 \(d^-(u)-d^+(u)=1\)
- 其余所有點的出度等於入度
歐拉圖的代碼實現
版子題,uoj #117. 歐拉回路 .
因為歐拉圖是很多個環的並 .
所以,對於每一個環,都可以輕松定下訪問的方向 .
此時,考慮如何將兩個環合並,首先,這兩個環能合並,必須要有點同時處於這兩個環上,此時,這兩個環的遍歷方案一定能被合並 .
dfs 的時候當前點 \(u\) 考慮直接找到一條沒有被訪問的邊 \((u,v)\) ,dfs 下去,肯定會在某個點再次訪問到 \(u\) ,此時,回溯的路徑一定構成一個歐拉回路 ( 一定是在 dfs 之后把 \(u\) 計入答案序列中 ) .
時間復雜度和空間復雜度都為 \(O(n+m)\) . 要加弧優化 . 不然會退化成 \(O(nm)\) .
#include<bits/stdc++.h>
using namespace std;
inline int rd(){
char ch=getchar();
while(ch<'0'||ch>'9')ch=getchar();
int res=0;
while(ch>='0'&&ch<='9'){
res=(res<<3)+(res<<1)+ch-'0';
ch=getchar();
}
return res;
}
inline void pr(int res){
if(res==0){
putchar('0');
return;
}
int a[10],len=0;
while(res>0){
a[len++]=res%10;
res/=10;
}
for(int i=len-1;i>=0;i--)
putchar(a[i]+'0');
}
int type;
int n,m;
vector<pair<int,int> >g[100010];
int iter[100010];
int deg[100010];
bool vis[200010];
int mark[100010];
stack<int>s;
namespace undirected{
void dfs(int x){
mark[x]++;
for(int &i=iter[x];i<(int)g[x].size();i++){
int to=g[x][i].first,id=g[x][i].second;
if(vis[abs(id)])continue;
vis[abs(id)]=true;
dfs(to);
s.push(id);
}
}
void work(){
n=rd();m=rd();
for(int i=1;i<=m;i++){
int u=rd()-1,v=rd()-1;
deg[u]++;
deg[v]++;
mark[u]=mark[v]=1;
g[u].push_back(make_pair(v,i));
g[v].push_back(make_pair(u,-i));
}
for(int i=0;i<n;i++){
if(deg[i]&1){
puts("NO");
return;
}
}
for(int i=0;i<n;i++){
if(mark[i]==1){
dfs(i);
break;
}
}
for(int i=0;i<n;i++){
if(mark[i]==1){
puts("NO");
return;
}
}
puts("YES");
while(!s.empty()){
if(s.top()<0)putchar('-');
pr(abs(s.top()));
putchar(' ');
s.pop();
}
putchar('\n');
}
}
namespace directed{
void dfs(int x){
mark[x]++;
for(int &i=iter[x];i<(int)g[x].size();i++){
int to=g[x][i].first,id=g[x][i].second;
if(vis[id])continue;
vis[id]=true;
dfs(to);
s.push(id);
}
}
void work(){
n=rd();m=rd();
for(int i=1;i<=m;i++){
int u=rd()-1,v=rd()-1;
deg[u]++;
deg[v]--;
mark[u]=mark[v]=1;
g[u].push_back(make_pair(v,i));
}
for(int i=0;i<n;i++){
if(deg[i]!=0){
puts("NO");
return;
}
}
for(int i=0;i<n;i++){
if(mark[i]==1){
dfs(i);
break;
}
}
for(int i=1;i<n;i++){
if(mark[i]==1){
puts("NO");
return;
}
}
puts("YES");
while(!s.empty()){
pr(s.top());
putchar(' ');
s.pop();
}
putchar('\n');
}
}
int main(){
type=rd();
if(type==1)undirected::work();
else directed::work();
return 0;
}
/*inline? ll or int? size? min max?*/
題目
1. cf1610f Mashtali: a Space Oddysey
這道題重點不在於歐拉回路,而是在於想到可以縮邊,此時可以用歐拉回路來縮邊 . 題解