竞赛图找哈密尔顿回路(hdu3414)

下面内容转自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算法提前判断。


#include<iostream>
#include<cstdio>
#include<string>
#include<cstring>
#include<vector>
#include<cmath>
#include<queue>
#include<stack>
#include<map>
#include<set>
#include<algorithm>
using namespace std;
const int maxn=1100;
int N;
int g[maxn][maxn];
int nex[maxn];
bool expend(int st)
{
    for(int i=0;i<N;i++)nex[i]=-1;
    int head=st,tail=head;
    for(int i=0;i<N;i++)
    {
        if(i==st)continue;
        if(g[i][head])nex[i]=head,head=i;
        else
        {
            int x=head,y=nex[head];
            while(y!=-1&&g[y][i])
            {
                x=y;
                y=nex[y];
            }
            nex[x]=i;
            nex[i]=y;
            if(y==-1)tail=i;
        }
    }
    if(g[tail][head])
    {
        nex[tail]=head;
        return true;
    }
    return false;
}
bool solve()
{
    for(int i=0;i<N;i++)
        if(expend(i))return true;
    return false;
}
int main()
{
    while(scanf("%d",&N)!=EOF,N)
    {
        for(int i=0;i<N;i++)
            for(int j=0;j<N;j++)
                scanf("%d",&g[i][j]);
        if(N==1){printf("1\n");continue;}
        if(N==2||!solve())printf("-1\n");
        else
            for(int i=0,j=0;i<N;i++,j=nex[j])
                printf("%d%c",j+1,i==N-1?'\n':' ');
    }
    return 0;
}





评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值