uoj #117. 歐拉回路 套圈法
阿新 • • 發佈:2019-02-02
題面
題意
給出一幅有向或無向圖,判斷是否存在一個環走遍所有邊,若有則輸出求其中的環.
做法
首先判斷很容易,對於無向圖只要聯通且度數都是偶數即可,對於有向圖,則要聯通且入度等於出度,問題在於如何找到這個環.
我們可以發現,如果隨便走的話,必然會回到出發點,但是不一定走過所有的邊,而剩下的圖恰是一條與我們之前走的路有交點的歐拉回路,沒有走完的原因就是我們之前有些地方走錯了,因此要退回到兩條路的焦點,也就是還有邊沒有走過的點,然後走完剩餘圖中的歐拉回路,再走之前倒退的路即可,用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();
}