poj2438 Children's Dining【哈密顿图模板题】

Description

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

这次的图论出的真全==这次又是我从没接触过的哈密顿图,这是啥?欧拉回路是每个边只能遍历一次,哈密顿是每个点只能遍历一次。

题意说要求讨厌的小孩不能挨着,那就让不讨厌的建边,反向建边就是这么来的==不是拓扑反向建边的那种反向,单纯的0-1 1-0而已

套模板

    #include<stdio.h>
    #include<string.h>
    #define M 407
using namespace std;
    int ans[M],g[M][M];
    bool map[M][M],vis[M];
    int n,m;

    void init()
    {
        for(int i=0;i<=n;i++)
            for(int j=0;j<=n;j++)
                if(i==j)
                    g[i][j]=0;
                else
                    g[i][j]=1;
        memset(vis,0,sizeof(vis));
        memset(ans,0,sizeof(ans));
    }

    void revese(int ans[M],int s,int t)//将ans数组中s到t的部分倒置
    {
        int temp;
        while(s<t)
        {
            temp=ans[s];
            ans[s]=ans[t];
            ans[t]=temp;
            s++;
            t--;
        }
    }

    void Hamilton()
    {
        int s=1,t;//初始化s取1号点
        int ansi=2;
        int i,j,w,temp;
        for(i=1;i<=n;i++)
            if(g[s][i])break;
        t=i;//取任意连接s的点为t
        vis[s]=vis[i]=true;
        ans[0]=s;
        ans[1]=t;
        while(true)
        {
            while(true)//从t向外扩展
            {
                for(i=1;i<=n;i++)
                    if(g[t][i]&&!vis[i])
                    {
                        ans[ansi++]=i;
                        vis[i]=true;
                        t=i;
                        break;
                    }
                if(i>n)break;
            }
            //将当前得到的序列倒置,s和t互换,从t继续扩展,相当于在原来的序列上从s扩展
            w=ansi-1;
            i=0;
            revese(ans,i,w);
            temp=s;
            s=t;
            t=temp;
            //从新的t向外扩展,相当于在原来的序列上从s向外扩展
            while(true)
            {
                for(i=1;i<=n;i++)
                    if(g[t][i]&&!vis[i])
                    {
                        ans[ansi++]=i;
                        vis[i]=true;
                        t=i;
                        break;
                    }
                if(i>n)break;
            }
            //如果s和t不相邻,进行调整
            if(!g[s][t])
            {
                for(i=1;i<ansi-2;i++)
                    if(g[ans[i]][t]&&g[s][ans[i+1]])//取序列中一点i,使得ans[i]与t相连接且ans[i+1]与s相连
                        break;
                //将从ans[i+1]到t部分的ans[]倒置
                w=ansi-1;
                i++;
                t=ans[i];
                revese(ans,i,w);
            }
            //如果当前s和t相连
            if(ansi==n)return;//如果当前序列中包含n个元素,算法结束
            //当前序列中的元素个数小于n,寻找点ans[i],使得ans[i]与ans[]外一点相连
            for(j=1;j<=n;j++)
            {
                if(vis[j])continue;
                for(i=1;i<ansi-2;i++)
                    if(g[ans[i]][j])
                        break;
                if(g[ans[i]][j])
                    break;
            }
            s=ans[i-1];
            t=j;
            revese(ans,0,i-1);//将ans[]中s到ans[i-1]部分的ans[]倒置
            revese(ans,i,ansi-1);//将ans[]中ans[i]到t的部分倒置
            ans[ansi++]=j;//将点j加入到ans[]的尾部
            vis[j]=true;
        }
    }

    int main()
    {
     //   freopen("cin.txt","r",stdin);
        while(scanf("%d%d",&n,&m),n|m)
        {
            n*=2;
            init();
            int a,b;
            for(int i=1;i<=m;i++)
            {
                scanf("%d%d",&a,&b);
                g[a][b]=g[b][a]=0;//巧妙的建立反图
            }
            Hamilton();
            printf("%d",ans[0]);
            for(int i=1;i<n;i++)
                printf(" %d",ans[i]);
            printf("\n");
        }
        return 0;
    }


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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值