1. 程式人生 > >hdu 4337——poj 2438(哈密爾頓迴路求解模板)

hdu 4337——poj 2438(哈密爾頓迴路求解模板)

               轉:http://imlazy.ycool.com/post.2072698.html

:設一個無向圖中有 N 個節點,若所有節點的度數都大於等於 N/2,則漢密爾頓迴路一定存在。注意,“N/2” 中的除法不是整除,而是實數除法。如果 N 是偶數,當然沒有歧義;如果 N 是奇數,則該條件中的 “N/2” 等價於 “⌈N/2⌉”。


證明起來不難。首先可以證明圖一定是連通的。設 d(v) 表示節點 v 的度數。對於任意兩個節點 u、 v,若它們不相鄰,則可能和它們相鄰的節點共有 N - 2 個,而 d(u) + d(v) ≥ N/2 + N/2 ≥ N,那麼根據鴿巢原理,肯定存在一個節點與 u 和 v 都相鄰。即證,任何兩個節點之間都是連通的。

接下來,證明漢密爾頓迴路存在的過程其實就是構造這條迴路的過程,分成以下幾個步驟:

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 相鄰。證明方法也是根據鴿巢原理,既然與 S 和 T 相鄰的點都在該路徑上,它們分佈的範圍只有 v1 ∼ vk 這 k 個點, k ≤ N - 2,而 d(S) + d(T) ≥ N,那麼可以想像,肯定存在一個與 S 相鄰的點 vi 和一個與 T 相鄰的點 vj, 滿足 j < i。那麼上面的命題也就顯然成立了。

找到了滿足條件的節點 vi
以後,就可以把原路徑變成 S → vi+1 → T → vi → S,即形成了一個迴路。
4. 現在我們有了一個沒有重複節點的迴路。如果它的長度為 N,則漢密爾頓迴路就找到了。

如果迴路的長度小於 N,由於整個圖是連通的,所以在該回路上,一定存在一點與迴路以外的點相鄰。那麼從該點處把迴路斷開,就變回了一條路徑。再按照步驟 1 的方法儘量擴充套件路徑,則一定有新的節點被加進來。接著回到步驟 2

在整個構造過程中,如果說每次到步驟 4 算是一輪的話,那麼由於每一輪當中,至少有一個節點被加入到路徑 S → T 中來,所以總的輪數肯定不超過 N 輪。實際上,不難看出該演算法的複雜度就是 O(N2),因為總共擴充套件了 N 步路徑,每步擴充套件最多列舉所有的節點。

Source Code

Problem: 2438  User: 1013101127 
Memory: 848K  Time: 141MS 
Language: C++  Result: Accepted 

Source Code 
#include <iostream>
#include <cstring>
#include <cstdio>
#include <cmath>
using namespace std;
const int N = 410;
int mp[N][N];
int ans[N];
int vis[N];
int start;
int end;
int n,m;
int cnt;
void init()
{
    for(int i=1;i<=n;i++)
     for(int j=1;j<=n;j++)
      if(i==j)mp[i][j]=0;
      else    mp[i][j]=1;
    memset(vis,0,sizeof(vis));
    memset(ans,0,sizeof(ans));
    cnt=0;
}

void Reverse(int s,int e)
{
    while(s<e)
    {
     swap(ans[s],ans[e]);
     s++;
     e--;
    }
}
void kuozhan()
{
    while(1)
    {
        int flag=0;
        for(int i=1;i<=n;i++)
        {
            if(!vis[i]&&mp[end][i])
            {
                ans[cnt++]=i;
                end=i;
                vis[i]=1;
                flag=1;
                break;
            }
        }
        if(!flag)
        break;
    }
}

void hamiltun()
{
   start=1;
   for(int i=1;i<=n;i++)
       if(mp[1][i]){
           end=i;break;
       }
    vis[start]=1;
    vis[end]=1;
    ans[0]=start;
    ans[1]=end;
    cnt=2;
    while(1)
    {
      kuozhan();
      Reverse(0,cnt-1);
      swap(start,end);
      kuozhan();
      int mid=0;
      if(!mp[start][end])
      {
          for(int i=1;i<cnt-2;i++)
          {
            if(mp[ans[i]][end]&&mp[ans[i+1]][start])
            {
                mid=i+1;
                break;
            }
          }
          Reverse(mid,cnt-1);
          end=ans[cnt-1];
      }
      if(cnt==n)break;
      for(int i=1;i<=n;i++)
      {
          if(!vis[i])
          {
            int j;
            for(j=1;j<cnt-1;j++)
                if(mp[ans[j]][i])
                 {mid=j;break;}
            if(mp[ans[mid]][i]){
             end=i;mid=j;break;
            }
          }
      }
      start=ans[mid-1];
      Reverse(0,mid-1);
      Reverse(mid,cnt-1);
      ans[cnt++]=end;
      vis[end]=1;
    }
}

int main()
{
    while(scanf("%d%d",&n,&m)!=EOF)
    {
        if(!n&&!m)break;
        n*=2;
        int u,v;
        init();
        for(int i=1;i<=m;i++)
        {
          scanf("%d%d",&u,&v);
          mp[u][v]=mp[v][u]=0;
        }
        hamiltun();
        cout<<ans[0];
        for(int i=1;i<cnt;i++)
          printf(" %d",ans[i]);
        cout<<endl;
    }
    return 0;
}