zoukankan      html  css  js  c++  java
  • 【模板】哈密顿回路Dirac定理模型——poj2438

    参考博客 https://blog.csdn.net/zhouzi2018/article/details/81278942?utm_medium=distribute.pc_relevant.none-task-blog-BlogCommendFromMachineLearnPai2-1.nonecase&depth_1-utm_source=distribute.pc_relevant.none-task-blog-BlogCommendFromMachineLearnPai2-1.nonecase

    概念:

      哈密顿图:图G的一个回路,若它通过图的每一个节点一次,且仅一次,就是哈密顿回路.

      存在哈密顿回路的图就是哈密顿图.哈密顿图就是从一点出发,经过所有点必须且只能一次,最终回到起点的路径.图中有的边可以不经过,但是不会有边被经过两次.

      与欧拉图的区别:欧拉图讨论的实际上是图上关于边的可行便利问题,而哈密顿图的要求与点有关.

    判定:

    一:Dirac定理(充分条件)

      设一个无向图中有N个顶点,若所有顶点的度数大于等于N/2,则哈密顿回路一定存在.(N/2指的是⌈N/2⌉,向上取整)

    二:基本的必要条件

      设图G=<V, E>是哈密顿图,则对于v的任意一个非空子集S,若以|S|表示S中元素的数目,G-S表示G中删除了S中的点以及这些点所关联的边后得到的子图,则W(G-S)<=|S|成立.其中W(G-S)是G-S中联通分支数.

    三:竞赛图(哈密顿通路)

      N(N>=2)阶竞赛图一点存在哈密顿通路.

    算法: 自己画图模拟一下就能理解

      1:任意找两个相邻的节点S和T,在其基础上扩展出一条尽量长的没有重复结点的路径.即如果T与结点v相邻,而且v不在路径S -> T上,则可以把该路径变成S -> T ->v,然后v成为新的T.从S和T分别向两头扩展,直到无法继续扩展为止,即所有与S或T相邻的节点都在路径S -> T上.

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

      3:若S与T不相邻,可以构造出来一个回路.

           设路径S -> T上有k+2个节点,依次为S, v1, v2, ..., vk, T.可以证明存在节点vi(i属于[1, k]),满足vi与T相邻,且vi+1与S相邻.

           找到这个节点vi,把原路径变成S -> vi -> T -> vi+1->S ,即形成了一个回路.

      4:到此为止,已经构造出来了一个没有重复节点的的回路,如果其长度为N,则哈密顿回路就找到了.

         如果回路的长度小于N,由于整个图是连通的,所以在该回路上,一定存在一点与回路之外的点相邻.那么从该点处把回路断开,就变回了一条路径,同时还可以将与之相邻的点加入路径.

         再回到步骤1进行重复

    步骤:

      1:初始化,令s = 1,t为s的任意一个邻接点.

      2:如果ans[]中元素的个数小于n,则从t开始向外扩展,如果有可扩展点v,放入ans[]的尾部,并且t=v,并继续扩展,如无法扩展进入步骤3.

      3:将当前得到的ans[]倒置,s和t互换,从t开始向外扩展,如果有可扩展点v,放入ans[]尾部,并且t=v,并继续扩展.如无法扩展进入步骤4.

      4:如果当前s和t相邻,进入步骤5.否则,遍历ans[],寻找点ans[i],使得ans[i]与t相连并且ans[i +1]与s相连,将ans[i+1..t]倒置,t=ans[i +1],进如步骤5.

      5:如果当前ans[]中元素的个数等于n,算法结束,ans[]中保存了哈密顿回路(可看情况是否加入点s).

        否则,如果s与t连通,但是ans[]中的元素的个数小于n,则遍历ans[],寻找点ans[i],使得ans[i]与ans[]外的一点(j)相连,则令s=ans[i - 1],t = j,

        将ans[s..i-1]倒置,将ans[i..t]倒置,将点j加入到ans[]的尾部,转步骤2.

    #include<cstring>
    #include<iostream>
    #include<cstdio>
    using namespace std;
    #define N 2005
    
    int mp[N][N],n,m,ans[N],vis[N];
    
    inline void reverse(int a[N],int s,int t){
        while(s<t)
            swap(a[s],a[t]),++s,--t;
    }
    int Hamilton(int a[N],int mp[N][N],int n){
        memset(vis,0,sizeof vis);
        int s=1,t,len=0,i,j;
        for(t=1;t<=n;t++)if(mp[s][t])break;
        vis[s]=vis[t]=1;
        ans[++len]=s;ans[++len]=t;//步骤1 
        while(1){
            int flag=0;
            while(1){ //步骤2:从t向外扩展 
                for(i=1;i<=n;i++)if(mp[t][i] && !vis[i]){
                    flag=1;
                    ans[++len]=i;t=i;
                    vis[i]=1;break;
                }
                if(i>n)break; 
            }
            reverse(ans,1,len);//步骤3:倒置数组,再扩展一次
            swap(s,t);
            while(1){
                for(i=1;i<=n;i++)if(mp[t][i] && !vis[i]){
                    flag=1;
                    ans[++len]=i;t=i;
                    vis[i]=1;break;
                }
                if(i>n)break;
            } 
            
            if(!mp[s][t]){//将步骤4:链改成环
                for(i=2;i<=len-2;i++)
                    if(mp[ans[i]][t] && mp[s][ans[i+1]])break;
                i++;t=ans[i];flag=1; 
                reverse(ans,i,len);
            }
            if(len==n)return 1;
            
            for(j=1;j<=n;j++)if(!vis[j]){//步骤5:找环外和环相连的点 
                for(i=2;i<=len-2;i++)if(mp[ans[i]][j])break;
                if(mp[ans[i]][j])break;
            }
            s=ans[i-1];t=j;//把环拆成链,加入新点 
            reverse(ans,1,i-1);
            reverse(ans,i,len);
            ans[++len]=j;vis[j]=1;
            if(!flag)return 0;
        }
    }
    
    int main(){
        while(scanf("%d%d",&n,&m)){
            if(!n)break;
            n<<=1;
            for(int i=1;i<=n;i++)
                for(int j=1;j<=n;j++)if(i!=j)
                    mp[i][j]=1;
            for(int i=1;i<=m;i++){
                int u,v;scanf("%d%d",&u,&v);
                mp[u][v]=mp[v][u]=0;
            }
            
            int res=Hamilton(ans,mp,n);
            if(!res){puts("No solution!");continue;}
            
            for(int i=1;i<=n;i++){
                cout<<ans[i];
                if(i!=n)cout<<" ";
            }
            puts("");puts(""); 
        }
    }

     

  • 相关阅读:
    mysql5.5 uuid做主键与int做主键的性能实测
    dom4j解析xml字符串实例
    spring自动注入是单例还是多例?单例如何注入多例?
    Spring中Bean的五个作用域
    【总结】瞬时高并发(秒杀/活动)Redis方案
    浅谈分布式事务
    基于Redis实现分布式锁
    MySQL事务隔离级别详解
    Redis学习手册(Sorted-Sets数据类型)
    Redis的快照持久化-RDB与AOF
  • 原文地址:https://www.cnblogs.com/zsben991126/p/12955975.html
Copyright © 2011-2022 走看看