下面内容转自http://blog.sina.com.cn/s/blog_8d84b9240101fdwj.html:
对于一个图中是否存在一条哈密顿路,没有可靠的充分必要条件(貌似邻接矩阵恒式可以?),因此求哈密顿路是一个NP问题,一般要使用搜索和状压dp求解,但汉密尔顿回路的存在有许多充分条件,即当图满足某些特定性质的时候,汉密尔顿回路一定存在,而且可以根据一些算法构造出来。
1.Dirac定理:设一个无向图中有 N 个节点,若所有节点的度数都大于等于 N/2,则汉密尔顿回路一定存在。
(“N/2” 中的除法不是整除,而是实数除法,该条件中的 “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(N^2),因为总共扩展了 N 步路径,每步扩展最多枚举所有的节点。
2.竞赛图:n(n>=2)阶竞赛图一定存在哈密顿通路
证明:对n作归纳法。n=2时,D的基图为K2,结论成立。设n=k时结论成立。现在设n=k+1.设V(D)={v1,v2,…,vk,vk+1}。令D1=D-vk+1,易知D1为k阶竞赛图,由归纳假设可知,D1存在哈密顿通路,设Г1=v'1v'2…v'k为其中一条。下面证明vk+1可扩到Г1中去。若存在v'r(1≤r≤k),有∈E(D),i=1,2,…,r-1,而∈E(D),见图(1)所示,则Г=v'1v'2…v'r-1vk+1v'r…v'k为D中哈密顿通路。否则,i∈{1,2,…,k},均有∈E(D),见下图所示,则Г=Г'∪为D中哈密顿通路。
==================================================================================================
总的来说,无论是有向图还是无向图,求汉密尔顿路都适合用前面介绍的“扩展”的办法。
而汉密尔顿回路就比较特殊了。
对于竞赛图(每两个点之间有且只有一条有向边),一定有汉密尔顿路,当且仅当竞赛图是强连通的,存在汉密尔顿回路,这个可以在“扩展”的过程中判断,也可以用Tarjan或Kosaraju算法提前判断。
[cpp] view plain copy print?
1. #include
2. #include
3. #include
4. #include
5. #include
6. #include
7. #include
8. #include
9. #include
10. #include
11. #include
12. using namespace std;
13. const int maxn=1100;
14. int N;
15. int g[maxn][maxn];
16. int nex[maxn];
17. bool expend(int st)
18. {
19. for(int i=0;i 20. int head=st,tail=head; 21. for(int i=0;i 22. { 23. if(i==st)continue; 24. if(g[i][head])nex[i]=head,head=i; 25. else 26. { 27. int x=head,y=nex[head]; 28. while(y!=-1&&g[y][i]) 29. { 30. x=y; 31. y=nex[y]; 32. } 33. nex[x]=i; 34. nex[i]=y; 35. if(y==-1)tail=i; 36. } 37. } 38. if(g[tail][head]) 39. { 40. nex[tail]=head; 41. return true; 42. } 43. return false; 44. } 45. bool solve() 46. { 47. for(int i=0;i 48. if(expend(i))return true; 49. return false; 50. } 51. int main() 52. { 53. while(scanf("%d",&N)!=EOF,N) 54. { 55. for(int i=0;i 56. for(int j=0;j 57. scanf("%d",&g[i][j]); 58. if(N==1){printf("1\n");continue;} 59. if(N==2||!solve())printf("-1\n"); 60. else 61. for(int i=0,j=0;i 62. printf("%d%c",j+1,i==N-1?'\n':' '); 63. } 64. return 0; 65. } Problem Description The city is so crowded that the mayor can't bear any longer. He issued an order to change all the roads into one-way street. The news is terrible for Jack, who is the director of a tourism company, because he has to change the travel route. All tourists want to set out from one scenic spot, then go to every scenic spots once and only once and finally return to the starting spot. They don’t care about which spot to start from, but they won’t go back to the starting spot before they have visited all other spots. Fortunately, the roads in the city have been perfectly built and any two scenic spots have been connected by ONE road directly. Jack gives the map of the city to you, and your task is to arrange a new travel route around the city which can satisfy the tourists. Input Input consists of multiple test cases and ends with a line of “0”. For each test case: The first line contains a single integer n (0 Then n lines follows, and each line consists of n integers. These n lines make a matrix. If the element in the ith row and the jth column is 1(i≠j), it means that the direction of the road between spot i and spot j is from spot i to spot j. If that element is 0, it means that the road’s direction is from spot j to spot i. The numbers in the main diagonal of the matrix are all 0. (i and j start from 1) Output For each test case, print all the spots No. according to the traveling order of the route in one line. If multiple routes exist, just print one of them. If no such route exists, print a “-1” instead. Because the starting spot is the same as the ending spot, so you don’t need to print the ending spot. This problem needs special judge. Sample Input 5 0 0 1 1 1 1 0 1 1 0 0 0 0 1 0 0 0 0 0 1 0 1 1 0 0 2 0 1 0 0 0 Sample Output 1 3 4 5 2 -1 ====================================================================================== 这道题到现在我心里还是虚的,poj3780明明是同一道题,相同的代码却wa了,让我怀疑自己是占特判数据的便宜水过的。 这道题首先想到的方法是暴力深搜,不出所料的tle了。 后来做了优化,竞赛图当且仅当强连通时是汉密尔顿图,于是我用Tarjan先把不是汉密尔顿图的去掉。 Tarjan是线性效率所以我并不担心。 然后我把入度为1的点跟它唯一的前驱缩成一个点,把出度为1的点跟它的唯一后继缩成一个点,还是深搜。 依然超时,看来深搜是怎么都不行了。 后来我看到了这个:http://blog.sina.com.cn/s/blog_8d84b9240101fdwj.html 于是有了下面的代码。 ====================================================================================== #include const int N = 1000; bool g[N][N]; int n , next[N]; bool expand( int s ) { for( int i = 0 ; i < n ; next[i++] = -1 ); int front = s , back = front; for( int i = 0 ; i < n ; i++ ) { if( i == s ) continue; if( g[i][front] ) next[i] = front , front = i; else { int a = front , b = next[front]; while( b != -1 && g[b][i] ) a = b , b = next[b]; next[a] = i; next[i] = b; if( b == -1 ) back = i; } } if( g[back][front] ) { next[back] = front; return true; } return false; } bool solve() { for( int i = 0 ; i < n ; i++ ) if( expand(i) ) return true; return false; } int main() { while( scanf("%d",&n) , n ) { for( int i = 0 ; i < n ; i++ ) for( int j = 0 ; j < n ; j++ ) scanf("%d",&g[i][j]); if( n == 1 ) puts("1"); else if( n == 2 || !solve() ) puts("-1"); else for( int i = 0 , j = 0 ; i < n ; i++ , j = next[j] ) printf("%d%c",j+1,i==n-1?'\n':' '); } return 0; }