哈密顿回路

哈密顿回路好多,其实不是很难,但是看了一天了。。看一会睡一会,什么状态啊。。 
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;
    }
}
        

 

posted @ 2013-01-25 20:31  Norcy  阅读(5194)  评论(1编辑  收藏  举报