哈密顿回路

哈密顿回路好多,其实不是很难,但是看了一天了。。看一会睡一会,什么状态啊。。 
View Code
/*

【题目来源】

http://poj.org/problem?id=2438

【题目分析】

有敌对关系的小朋友,不能坐在一起。最后围成一个圈,吃饭。。。

将小朋友看成点,有敌对关系的看成没有边,最后构成一个回路。

哈密顿回路。 



【小小总结】 

哈密顿回路

充分条件:

无向连通图中任意2点度数之和大于等于顶点数,则必定存在哈密顿回路。 



思路分析: 

1.任意找两个相邻的节点S和T,在它们基础上扩展出一条尽量长的没有重复节点的路径。

也就是说,如果S与节点v相邻,而且v不在路径S->T上,则可以把该路径变成v->S->T,然后v成为新的S。

从S和T分别向两头扩展,直到无法扩为止,即所有与S或T相邻的节点都在路径S->T上。

2.若S与T相邻,则路径S->T形成了一个回路。

3.若S与T不相邻,可以构造出一个回路。设路径S->T上有k+2个节点,依次为S、v1、v2……vk和T。

可以证明v1到vk中必定存在vi,满足vi与T相邻,且vi+1与S相邻。(其实vi,vi+1与s t同时相邻) (怎么证明就不赘述了,反正刷题肯定不会叫你证)

找到了满足条件的节点vi以后,就可以把原路径变成S->vi+1->T->vi->S,即形成了一个回路。(自己画图就知道了) 

4.现在我们有了一个没有重复节点的回路。如果它的长度为N,则哈密顿回路就找到了。

如果回路的长度小于N,由于整个图是连通的,所以在该回路上,一定存在一点与回路以外的点相邻。

那么从该点处把回路断开,就变回了一条路径。再按照步骤1的方法尽量扩展路径,则一定有新的节点被加进来。(画图就知道了) 

接着回到步骤2。 



伪代码: 

思路清楚后主要是理解好伪代码,伪代码一懂代码就写出来了。关于下面步骤中为什么要倒置,自己画画图就清楚了。 

s为哈密顿回路起点,t为当前哈密顿回路的终点,ans[]就是哈密顿回路啦,默认不包含0顶点

1.初始化,令s=1,t为任意与s相邻的点。

2.若ans[]中的元素个数小于n,则从t开始扩展,若可扩展,则把新点v加入ans[],并令t=v,继续扩展到无法扩展。

3.将ans[]倒置,s,t互换,从t(原来的s)开始扩展,若可扩展,则把新点v加入ans[],并令t=v,继续扩展到无法扩展。

4.此时s,t两头都无法扩展了,若s,t相连,则继续步骤5。若st不相连,则遍历ans[],必定会有2点,ans[i]与t相连,ans[i+1]与s相连,

将ans[i+1]到t倒置,t=ans[i+1](未倒置前的)

5.st相连,此时为一个环。若ans[]个数等于n,算法结束,ans[]为哈密顿回路,如需要再添加一个起点。

若ans[]个数小于n,遍历ans[],寻找ans[i],使得ans[i]与ans[]外一点j相连,倒置ans[]中s到ans[i-1]部分,令s = ans[i-1],

再倒置ans[]中ans[i]到t的部分,j加入ans[],t = j.继续步骤2 



下面去掉main函数,就是求解哈密顿回路的模版了。 

*/

#include <iostream>

#include <cstring>

#include <algorithm>

using namespace std;



#define Max 500



int map[Max][Max];

int ans[Max];

bool vis[Max];



//ans数组的index 

int index;

int n, m;

int s, t;



void init()

{

    for (int i = 0; i < Max; ++i)

        for (int j = 0; j < Max; ++j)

            if (i == j)

                map[i][j] = 0;

            else

                map[i][j] = 1;

    

    memset(ans, 0, sizeof(ans));

    memset(vis, 0 , sizeof(vis));

    

    index = 0;

}



void reverse(int a, int b)

{

    while (a < b)

    {

        swap(ans[a], ans[b]);

        

        a++;

        

        b--;

    }

}



void expand()

{

    while (true)

    {

        int i;

        

        for (i = 1; i <= n; ++i) 

        {

            if (!vis[i] && map[i][t])//未被访问且与t相连 

            {

                ans[index++] = i;

                

                vis[i] = true;

                

                t = i;

                

                break;

            }

        }

        

        if (i > n) break;//无法扩展 

    }

}



void Hamilton()

{

    //初始化s = 1

    s = 1; 

    

    //取任意连接s的点 

    for (int i = 1; i <= n; ++i)

    {

        if (map[i][s])

        {

            t = i;

            

            break;

        }

    }

    

    vis[s] = true;

    

    vis[t] = true;

    

    ans[index++] = s;

    

    ans[index++] = t;

    

    

    while (true)

    {

        //从t向外扩展 

        expand(); 

        

        //t扩展完毕,倒置ans并交换s,t 

        reverse(0, index-1);

        

        swap(s, t);

        

        //从另一头,t(原来的s)继续扩展 

        expand();

        

        //若s,t不相连,处理成相连 

        if (!map[s][t])

        {

            //在ans[1]到ans[index-2]中寻找两个相邻的且与st同时相连的点(必存在) 因为涉及i+1所以i < index-2 

            for (int i = 1; i < index-2; ++i) 

            {

                if (map[ans[i+1]][s] && map[ans[i]][t])

                {

                    reverse(i+1, index-1);//倒置ans[i+1]到ans[index-1] 

                    

                    t = ans[index-1];//更新t 

                    

                    break;

                }

            }

        }

        

        //若ans元素有n个,说明算法完成 

        if (index == n) return;

        

        //若ans元素不满n个,ans[]中寻找与未被遍历过的点相连的点,但这一点必定不是s,t.因为s,t已经遍历到无法遍历才能走到这一步 

        for (int j = 1; j <= n; ++j)

        {

            if (!vis[j])

            {

                int i;

                for (i = 1; i < index-1; ++i)//排除st 

                {

                    if (map[ans[i]][j])

                    {

                        s = ans[i-1];

                        

                        t = j;

                        

                        reverse(0, i-1);

                        

                        reverse(i,index-1);

                        

                        ans[index++] = j;

                        

                        vis[j] = true;

                        

                        break;

                    }

                }

                

                if (map[ans[i]][j])break;//记得有2个循环,要break两次 

            }

        }

        

        //继续返回,从t扩展。。 

    }

}

    

int main()

{

    while (cin >> n >> m, n||m)

    {

        n *= 2;

        

        init();

        

        int temp1, temp2;

        for (int i = 0; i < m; ++i)

        {

            cin >> temp1 >> temp2;

            

            map[temp1][temp2] = 0;

            

            map[temp2][temp1] = 0;

        }

        

        Hamilton();

        

        cout << ans[0];

        for (int i = 1; i < index; ++i)

            cout << ' ' << ans[i];

        

        cout << endl;

    }

}

        

 

你可能感兴趣的:(哈密顿回路)