【模板】哈密顿回路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(""); 
    }
}

 

原文地址:https://www.cnblogs.com/zsben991126/p/12955975.html