组合补考的我且先装个比,竞赛图中求汉密尔顿圈算法设计

 

竞赛图找哈密尔顿回路

 

下面内容转自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'2v'k为其中一条。下面证明vk+1可扩到Г1中去。若存在v'r(1rk),有E(D),i=1,2,,r-1,而E(D),见图(1)所示,则Г=v'1v'2v'r-1vk+1v'rv'k为D中哈密顿通路。否则,i{1,2,,k},均有E(D),见下图所示,则Г=Г'为D中哈密顿通路。


==================================================================================================

总的来说,无论是有向图还是无向图,求汉密尔顿路都适合用前面介绍的扩展的办法。

而汉密尔顿回路就比较特殊了。

对于竞赛图(每两个点之间有且只有一条有向边),一定有汉密尔顿路,当且仅当竞赛图是强连通的,存在汉密尔顿回路,这个可以在扩展的过程中判断,也可以用Tarjan或Kosaraju算法提前判断。

 

 

[cpp] view plain copy print?

1. #include<iostream>  

2. #include<cstdio>  

3. #include<string>  

4. #include<cstring>  

5. #include<vector>  

6. #include<cmath>  

7. #include<queue>  

8. #include<stack>  

9. #include<map>  

10. #include<set>  

11. #include<algorithm>  

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<N;i++)nex[i]=-1;  

20.     int head=st,tail=head;  

21.     for(int i=0;i<N;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<N;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<N;i++)  

56.             for(int j=0;j<N;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<N;i++,j=nex[j])  

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 dont care about which spot to start from, but they wont 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(ij), 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 roads 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 -1instead. Because the starting spot is the same as the ending spot, so you dont 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<cstdio>

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;

}

 

 

 

  • 2
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值