哈密頓回路好多,其實不是很難,但是看了一天了。。看一會睡一會,什么狀態啊。。

/* 【題目來源】 http://poj.org/problem?id=2438 【題目分析】 有敵對關系的小朋友,不能坐在一起。最后圍成一個圈,吃飯。。。 將小朋友看成點,有敵對關系的看成沒有邊,最后構成一個回路。 哈密頓回路。 【小小總結】 哈密頓回路 充分條件: 無向連通圖中任意2點度數之和大於等於頂點數,則必定存在哈密頓回路。 思路分析: 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。 可以證明v1到vk中必定存在vi,滿足vi與T相鄰,且vi+1與S相鄰。(其實vi,vi+1與s t同時相鄰) (怎么證明就不贅述了,反正刷題肯定不會叫你證) 找到了滿足條件的節點vi以后,就可以把原路徑變成S->vi+1->T->vi->S,即形成了一個回路。(自己畫圖就知道了) 4.現在我們有了一個沒有重復節點的回路。如果它的長度為N,則哈密頓回路就找到了。 如果回路的長度小於N,由於整個圖是連通的,所以在該回路上,一定存在一點與回路以外的點相鄰。 那么從該點處把回路斷開,就變回了一條路徑。再按照步驟1的方法盡量擴展路徑,則一定有新的節點被加進來。(畫圖就知道了) 接着回到步驟2。 偽代碼: 思路清楚后主要是理解好偽代碼,偽代碼一懂代碼就寫出來了。關於下面步驟中為什么要倒置,自己畫畫圖就清楚了。 s為哈密頓回路起點,t為當前哈密頓回路的終點,ans[]就是哈密頓回路啦,默認不包含0頂點 1.初始化,令s=1,t為任意與s相鄰的點。 2.若ans[]中的元素個數小於n,則從t開始擴展,若可擴展,則把新點v加入ans[],並令t=v,繼續擴展到無法擴展。 3.將ans[]倒置,s,t互換,從t(原來的s)開始擴展,若可擴展,則把新點v加入ans[],並令t=v,繼續擴展到無法擴展。 4.此時s,t兩頭都無法擴展了,若s,t相連,則繼續步驟5。若st不相連,則遍歷ans[],必定會有2點,ans[i]與t相連,ans[i+1]與s相連, 將ans[i+1]到t倒置,t=ans[i+1](未倒置前的) 5.st相連,此時為一個環。若ans[]個數等於n,算法結束,ans[]為哈密頓回路,如需要再添加一個起點。 若ans[]個數小於n,遍歷ans[],尋找ans[i],使得ans[i]與ans[]外一點j相連,倒置ans[]中s到ans[i-1]部分,令s = ans[i-1], 再倒置ans[]中ans[i]到t的部分,j加入ans[],t = j.繼續步驟2 下面去掉main函數,就是求解哈密頓回路的模版了。 */ #include <iostream> #include <cstring> #include <algorithm> using namespace std; #define Max 500 int map[Max][Max]; int ans[Max]; bool vis[Max]; //ans數組的index int index; int n, m; int s, t; void init() { for (int i = 0; i < Max; ++i) for (int j = 0; j < Max; ++j) if (i == j) map[i][j] = 0; else map[i][j] = 1; memset(ans, 0, sizeof(ans)); memset(vis, 0 , sizeof(vis)); index = 0; } void reverse(int a, int b) { while (a < b) { swap(ans[a], ans[b]); a++; b--; } } void expand() { while (true) { int i; for (i = 1; i <= n; ++i) { if (!vis[i] && map[i][t])//未被訪問且與t相連 { ans[index++] = i; vis[i] = true; t = i; break; } } if (i > n) break;//無法擴展 } } void Hamilton() { //初始化s = 1 s = 1; //取任意連接s的點 for (int i = 1; i <= n; ++i) { if (map[i][s]) { t = i; break; } } vis[s] = true; vis[t] = true; ans[index++] = s; ans[index++] = t; while (true) { //從t向外擴展 expand(); //t擴展完畢,倒置ans並交換s,t reverse(0, index-1); swap(s, t); //從另一頭,t(原來的s)繼續擴展 expand(); //若s,t不相連,處理成相連 if (!map[s][t]) { //在ans[1]到ans[index-2]中尋找兩個相鄰的且與st同時相連的點(必存在) 因為涉及i+1所以i < index-2 for (int i = 1; i < index-2; ++i) { if (map[ans[i+1]][s] && map[ans[i]][t]) { reverse(i+1, index-1);//倒置ans[i+1]到ans[index-1] t = ans[index-1];//更新t break; } } } //若ans元素有n個,說明算法完成 if (index == n) return; //若ans元素不滿n個,ans[]中尋找與未被遍歷過的點相連的點,但這一點必定不是s,t.因為s,t已經遍歷到無法遍歷才能走到這一步 for (int j = 1; j <= n; ++j) { if (!vis[j]) { int i; for (i = 1; i < index-1; ++i)//排除st { if (map[ans[i]][j]) { s = ans[i-1]; t = j; reverse(0, i-1); reverse(i,index-1); ans[index++] = j; vis[j] = true; break; } } if (map[ans[i]][j])break;//記得有2個循環,要break兩次 } } //繼續返回,從t擴展。。 } } int main() { while (cin >> n >> m, n||m) { n *= 2; init(); int temp1, temp2; for (int i = 0; i < m; ++i) { cin >> temp1 >> temp2; map[temp1][temp2] = 0; map[temp2][temp1] = 0; } Hamilton(); cout << ans[0]; for (int i = 1; i < index; ++i) cout << ' ' << ans[i]; cout << endl; } }