1. 程式人生 > >uoj #117. 歐拉回路 套圈法

uoj #117. 歐拉回路 套圈法

題面

題意

給出一幅有向或無向圖,判斷是否存在一個環走遍所有邊,若有則輸出求其中的環.

做法

首先判斷很容易,對於無向圖只要聯通且度數都是偶數即可,對於有向圖,則要聯通且入度等於出度,問題在於如何找到這個環.
我們可以發現,如果隨便走的話,必然會回到出發點,但是不一定走過所有的邊,而剩下的圖恰是一條與我們之前走的路有交點的歐拉回路,沒有走完的原因就是我們之前有些地方走錯了,因此要退回到兩條路的焦點,也就是還有邊沒有走過的點,然後走完剩餘圖中的歐拉回路,再走之前倒退的路即可,用dfs不斷反覆上述過程即可.
這裡要注意一個優化,一個點已經走過的邊不重複遍歷(否則最劣會退化為O(m^2)),也就是修改每個點的第一條邊(詳見程式碼).

程式碼

#include<iostream>
#include<cstdio>
#include<cstring>
#define GG return void(puts("NO"));
#define N 100100
using namespace std;

int T,n,m,bb=1,first[N],cnt,ans[N<<1],ds[N];
struct Bn
{
    int to,next;
    bool vis;
}bn[N<<2];

inline void add(int u,int v)
{
    bb++;
    bn[bb].to=v;
    bn[bb].next=first[u];
    first[u]=bb;
}

namespace
solve1 { void dfs(int now) { int p,q; for(p=first[now];p!=-1;p=first[now]) { first[now]=bn[p].next;//重要優化 if(bn[p].vis) continue; bn[p].vis=bn[p^1].vis=1; dfs(bn[p].to); ans[++cnt]=p/2; if(p%2) ans[cnt]*=-1
; } } void work() { int i,j,p,q; cin>>n>>m; for(i=1;i<=m;i++) { scanf("%d%d",&p,&q); add(p,q),add(q,p); ds[p]++,ds[q]++; } for(i=1;i<=n;i++) { if(ds[i]%2) GG; } for(i=1;i<=n;i++) { if(ds[i]) { dfs(i); break; } } if(cnt!=m) GG; puts("YES"); for(i=cnt;i>=1;i--) { printf("%d ",ans[i]); } } } namespace solve2 { void dfs(int now) { int p,q; for(p=first[now];p!=-1;p=first[now]) { first[now]=bn[p].next;//重要優化 if(bn[p].vis) continue; bn[p].vis=1; dfs(bn[p].to); ans[++cnt]=p-1; } } void work() { int i,j,p,q; cin>>n>>m; for(i=1;i<=m;i++) { scanf("%d%d",&p,&q); add(p,q),ds[p]++,ds[q]--; } for(i=1;i<=n;i++) { if(ds[i]) GG; } for(i=1;i<=n;i++) { if(first[i]!=-1) { dfs(i); break; } } if(cnt!=m) GG; puts("YES"); for(i=cnt;i>=1;i--) { printf("%d ",ans[i]); } } } int main() { memset(first,-1,sizeof(first)); int i,j; cin>>T; T==1?solve1::work():solve2::work(); }