概念:
哈密顿图:图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(""); } }