Children's Dining POJ2438 哈密顿图

题目描述:

Usually children in kindergarten like to quarrel with each other. This situation annoys the child-care women. For instant, when diner time comes, a fierce conflict may break out when a certain couple of children sitting side by side who are hostile with each other. Although there aren’t too many children dining at the same round table, but the relationship of “enemy” or “friend” may be very complex. The child-care women do come across a big problem. Now it is time for you to help them to figure out a proper arrangement of sitting, with which no two “enemy” children is adjacent.

Now we assume that there are 2 * n children who sit around a big table, and that none has more than n - 1 “enemies”.

输入输出:

Input
The input is consisted of several test blocks. For each block, the first line contains two integers n and m (1 <= n <= 200, 0 <= m <= n (n - 1)). We use positive integers from 1 to 2 * n to label the children dining round table. Then m lines followed. Each contains positive integers i and j ( i is not equal to j, 1 <= i, j <= 2 * n), which indicate that child i and child j consider each other as “enemy”. In a input block, a same relationship isn’t given more than once, which means that if “i j” has been given, “j i” will not be given.

There will be a blank line between input blocks. And m = n = 0 indicates the end of input and this case shouldn’t be processed.
Output
For each test block, if the proper arrangement exist, you should print a line with a proper one; otherwise, print a line with “No solution!”.
Sample Input

1 0

2 2
1 2
3 4

3 6
1 2
1 3
2 4
3 5
4 6
5 6

4 12
1 2
1 3
1 4
2 5
2 6
3 7
3 8
4 8
4 7
5 6
5 7
6 8

0 0

Sample Output

1 2
4 2 3 1
1 6 3 2 5 4
1 6 7 2 3 4 5 8

题目分析:

这道题的大意是有2*n个小孩,其中有m个敌对关系,这2*n个小孩需要排成一个圈,其相邻的人不能存在敌对关系,输出其任意一种可能的排列方式。
这道题是哈密顿图的模板题,刚看这道题我也不了解这道题的做法,之后发现这是一道哈密顿图的题,因为哈密顿图没有充要的判定条件,所以看上去判断是比较困难的。一起来学习一下这个特殊的图吧。

代码如下:

#include <iostream>
#include <string.h>
#include <stdio.h>

const int MAXN=410;
using namespace std;

int m,n;
bool map[MAXN][MAXN];
int ans[MAXN];
bool vis[MAXN];
int start,end;
int cnt;

void init()//初始化
{
    for(int i=1; i<=n; i++)
    {
        for(int j=1; j<=n; j++)
        {
            if (i==j) map[i][j]=0;
            else map[i][j]=1;//没有敌对关系的点标记为1
        }
    }
    memset(ans,0,sizeof(ans));
    memset(vis,0,sizeof(vis));
    cnt=0;
}


void reverse(int s,int e)//将ans数组中s到e的部分倒置
{
    while(s<e)
    {
        swap(ans[s],ans[e]);
        s++;
        e--;
    }
}

void kuozhan()//从end开始拓展
{
    while(1)
    {
        int flag=0;
        for(int i=1;i<=n;i++)
        {
            if(!vis[i] && map[end][i])
            {
                ans[cnt++]=i;
                end=i;
                vis[i]=1;
                flag=1;
                break;
            }
        }
        if(!flag)
        break;
    }
}//将当前得到的序列倒置,s和t互换,从t继续扩展,相当于在原来的序列上从s扩展

void hamiltun()
{
    start=1;//初始化start为1号点
    for(int i=1; i<=n; i++)
    {
        if (map[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(!map[start][end])//start end不相邻,调整
        {
            for(int i=1; i<cnt-2; i++)
            {
                if(map[ans[i]][end]&&map[ans[i+1]][start])//取序列中一点i,
    //使ans[i]与end相连接,ans[i+1]与start相连
                {
                    mid=i+1;
                    break;
                }
            }
            reverse(mid,cnt-1);//将ans[mid]到ans[end-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(map[ans[j]][i])
                    {
                        mid=j;
                        break;
                    }
                if(map[ans[mid]][i])
                {
                    end=i;
                    mid=j;
                    break;
                }
            }
        }
        start=ans[mid-1];
        reverse(0,mid-1);//从a[0]到ans[mid-1]倒置
        reverse(mid,cnt-1);//倒置ans[mid]到ans[end-1]倒置
        ans[cnt++]=end;//end点加入ans尾部
        vis[end]=1;//标记已经处理的点
    }
}

int main()
{
    while(~scanf("%d%d",&n,&m))
    {
        if (!m && !n) break;
        int u,v;
        n*=2;
        init();
        for(int i=1; i<=m; i++)
        {
            scanf("%d%d",&u,&v);
            map[u][v]=map[v][u]=0;//将有敌对关系的点标记为0
        }
        hamiltun();
        printf("%d ",ans[0]);
        for(int i=1;i<n;i++)
        {
            printf("%d ",ans[i]);
        }
        printf("\n");
    }
    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值