hdu 4337——poj 2438(哈密爾頓迴路求解模板)
阿新 • • 發佈:2019-02-03
轉: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 |
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;
}