uoj #117. 欧拉回路 套圈法

题面

题意

给出一幅有向或无向图,判断是否存在一个环走遍所有边,若有则输出求其中的环.

做法

首先判断很容易,对于无向图只要联通且度数都是偶数即可,对于有向图,则要联通且入度等于出度,问题在于如何找到这个环.
我们可以发现,如果随便走的话,必然会回到出发点,但是不一定走过所有的边,而剩下的图恰是一条与我们之前走的路有交点的欧拉回路,没有走完的原因就是我们之前有些地方走错了,因此要退回到两条路的焦点,也就是还有边没有走过的点,然后走完剩余图中的欧拉回路,再走之前倒退的路即可,用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();
}

你可能感兴趣的:(算法)