• 哈密顿回路


    哈密顿回路好多,其实不是很难,但是看了一天了。。看一会睡一会,什么状态啊。。 
    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;
        }
    }
            
  • 相关阅读:
    带提示范围的猜数小游戏--python
    python中字符串的常见操作
    html表单相关标签及属性
    CSS常用属性
    python装饰器
    python闭包的概念及使用
    nuxt.js实战踩坑记录
    vuex填坑记录
    prerender-spa-plugin预处理vue项目实践
    node+express第一次实战踩坑记录
  • 原文地址:https://www.cnblogs.com/chenyg32/p/2877252.html
Copyright © 2020-2023  润新知