给出一幅有向或无向图,判断是否存在一个环走遍所有边,若有则输出求其中的环.
首先判断很容易,对于无向图只要联通且度数都是偶数即可,对于有向图,则要联通且入度等于出度,问题在于如何找到这个环.
我们可以发现,如果随便走的话,必然会回到出发点,但是不一定走过所有的边,而剩下的图恰是一条与我们之前走的路有交点的欧拉回路,没有走完的原因就是我们之前有些地方走错了,因此要退回到两条路的焦点,也就是还有边没有走过的点,然后走完剩余图中的欧拉回路,再走之前倒退的路即可,用dfs不断反复上述过程即可.
这里要注意一个优化,一个点已经走过的边不重复遍历(否则最劣会退化为O(m^2)),也就是修改每个点的第一条边(详见代码).
#include
#include
#include
#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();
}