有向图欧拉回路的快速算法(POJ 2230题解)

高考考完开始进行算法竞赛的康复训练(好吧实际上是从零开始直接学,过完高三什么都忘了T T),在POJ上作了一些水题。今天作到这道欧拉回路,颇有感触,由于第一次学这个算法的时候并无学透,今天再作才发现原来求欧拉回路的算法有这么精妙。git

先讲题意吧,原文连接:http://poj.org/problem?id=2230。大意是给你一个无向图,要求找一条路径,走过每一条边刚好两次,且每次走的方向不一样。很容易就能想到把这图转化为有向图求欧拉回路。题目保证必定能找到从1点出发回到1点的答案。算法

如今的关键就是求这个回路了,一个朴素的想法就是直接DFS,每次通过一条边就删除它,回退的时候再还原。若是通过了原图边数刚好两倍就找到了一个答案。但这个算法实在太慢,直接TLE。segmentfault

如今咱们考虑搜索的过程:若是不还原一个点所遍历的边,那么当这个点去掉全部边以后,再去掉这个点,剩下的图是什么?先上图,这是题目所给的样例的草图:
还没遍历时的图像
而后从1开始,咱们丧心病狂一点,直接把1就搞掉不碰与1无关的边,反正遍历的顺序是不肯定的,这么作何尝不可。那么就变成了这样:
删除1和相关的边的状况
咱们看到,剩下的仍是一个欧拉回路。咱们能够猜测,一个欧拉回路,删掉一个点,仍然是一个欧拉回路。这个结论其实很是容易证实,随便想一想就明白这是对的。进一步研究,会发现一个更广泛的结论:从一个欧拉回路中拖走一个小欧拉回路,结果也是一个欧拉回路。这个性质也很明显,并且前一个结论是后一个结论的推论。由这个结论,咱们能够考虑,有公共点的两个欧拉回路实际上是能够拼接的。因而下面这个搜索算法就很好理解了:spa

void DFS(int now)
{
    for (int p=G.Head[now];p;p=G.Pre[p])
    {
        if (!G.Vis[p])
        {
            G.Vis[p]=1;
            DFS(G.V[p]);
        }
    }
    printf("%d\n",now);
}

G是图,我用了一个邻接表,G.Vis是标记这条边是否走过。若是一条边还没走过,就标记而后走下去,关键在若是一个点已经走完了怎么办:直接输出。每次从v出发回到v,就是扒走了一个回路,根据栈的性质,这样获得的顺序实际上是相反的。不过因为这题的图中边是成对出现,因此不要紧,倒过来也是能够的。输出的过程就是把一个个欧拉回路拼在一块儿。对于无向图,经过拼接也能够获得欧拉回路,不过相对于这个算法要复杂很多。最后提供Gitcafe代码连接,若有须要参考请看:https://gitcafe.com/linmx0130/OJCode/blob/master/POJ/P2230/main.cppcode

相关文章
相关标签/搜索