POJ-2438 哈密頓環 哈密瓜很甜

無向圖經過所有的節點一次且僅一次:哈密瓜路徑

無向圖經過所有的邊一次且僅一次:歐拉路徑

帶權哈密頓路徑就是旅行商問題。

參考博客:https://blog.csdn.net/zhouzi2018/article/details/81278942

過程:

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.可以證明存在節點vi(i屬於[1, k]),滿足vi與T相鄰,且vi+1與S相鄰.找到這個節點vi,把原路徑變成S -> vi -> T -> vi+1 ,即形成了一個迴路.

4:到此爲止,已經構造出來了一個沒有重複節點的的迴路,如果其長度爲N,則哈密頓迴路就找到了.如果迴路的長度小於N,由於整個圖是連通的,所以在該回路上,一定存在一點與迴路之外的點相鄰.那麼從該點處把迴路斷開,就變回了一條路徑,同時還可以將與之相鄰的點加入路徑.再按照步驟1的方法儘量擴展路徑,則一定有新的節點被加進來.接着回到路徑2.

證明:

跟據鴿巢定理,既然與S和T相鄰的點都在路徑上,它們分佈的範圍只有v1,v2---,vk這k個點,k<=N-2,跟據哈密頓迴路的第一個判斷條件,d(S)+d(T)>=N,那麼v1,v2,---vk這k個點中一定有至少2個點同時與S和T相連,根據鴿巢定理,肯定存在一個與S相鄰的點vi和一個與T相鄰的點vj,滿足j=i+1

僞代碼:

設s爲哈密頓迴路的起始點,t爲哈密頓迴路中終點s之前的點.ans[]爲最終的哈密頓迴路.倒置的意思指的是將數組對應的區間中數字的排列順序反向.

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部分的ans[]倒置,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到ans[i - 1]部分的ans[]倒置,將ans[]中的ans[i]到t的部分倒置,將點j加入到ans[]的尾部,轉步驟2.

#include<cstring>
#include<iostream>
#include<cstdio>
#include<cmath>
using namespace std;

const int maxn=410;
int ans[maxn];
int visit[maxn];// in case of reoccurrence
int friendt[maxn][maxn];
int n,m;
int s,t;
int num;

void rever(int s,int t){
    while(s<t){
        int tmp=ans[s];
        ans[s]=ans[t];
        ans[t]=tmp;
        s++;t--;
    }
}

void hamilton()
{
    s=1;
    for(int i=1;i<=n;i++){
        if(friendt[s][i]){t=i;break;}
    }
    ans[0]=s;ans[1]=t;
    num=2;
    //cout<<s<<" "<<t<<endl;
    memset(visit,0,sizeof(visit));
    visit[s]=1;visit[t]=1;
    int v;
    while(1){
    while(1){
        v=0;
        for(int i=1;i<=n;i++){
            if(friendt[t][i]&&!visit[i]){v=i;break;}
        }
        if(v){
            ans[num++]=v;t = v;visit[v]=1;
        }
        else break;
    }
    //cout<<t<<endl;
    rever(0,num-1);int tmp=s;s=t;t=tmp;
    while(1){
        v=0;
        for(int i=1;i<=n;i++){
            if(friendt[t][i]&&!visit[i]){v=i;break;}
        }
        if(v){
            ans[num++]=v;t = v;visit[v]=1;
        }
        else break;
    }
    if(!friendt[s][t]){
        for(int i=1;i<num-2;i++){
            if(friendt[ans[i]][t]&&friendt[ans[i+1]][s]){
                rever(i+1,num-1);t=ans[i+1];break;
            }
        }
    }
    if(num==n)return;
    int ind,j;
    for(int i=1;i<=n;i++){if(!visit[i]){j=i;break;}}
    for(int k=1;k<num-2;k++){
        if(friendt[ans[k]][j]){ind=k;break;}
    }
    s=ans[ind-1];t=j;rever(0,ind-1);rever(ind,num-1);
    ans[num++]=j;visit[j]=1;
    }
}

int main()
{
    //freopen("in.txt","r",stdin);
    while(cin>>n>>m&&(n||m)){
        n = 2*n;
        //cout<<n<<endl;
        for(int i=1;i<=n;i++)
            for(int j=1;j<=n;j++)
                {if(j==i)friendt[i][j]=0;else friendt[i][j]=1;}
        for(int i=0;i<m;i++){
            int a,b;
            scanf("%d%d",&a,&b);
            friendt[a][b]=0;
            friendt[b][a]=0;
        }
        num=0;
        hamilton();
        for(int i=0;i<n;i++)printf("%d ",ans[i]);
        cout<<endl;
    }
    return 0;
}

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章