Children's Dining(POJ 2438)---哈密尔顿回路序列输出

题目链接

题目描述

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”.

输入格式

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.

输出格式

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!”.

输入样例

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

输出样例

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

题意

题目大意有2*N个孩子坐在一个圆桌上吃饭,要求相邻的两个孩子不是敌人,且每个人最多有N-1个敌人,求一个满足要求的序列。

分析

典型的哈密尔顿回路模板题。由题中每个人最多有N-1个敌人,可知每个顶点的度至少为N+1,满足任意不相邻的两个顶点u,v,都有degree[u]+degree[v]≥N,所以在本题中必定存在哈密尔顿回路。
按照题目中给出的图G建立反图G‘,对反图按照Dirac定理扩展序列,求满足题意的哈密尔顿回路。

Dirac定理构造无向图哈密尔顿回路

  • 任意找两个相邻的节点 S 和 T,在其基础上扩展出一条尽量长的没有重复结点的路径,即如果 S 与结点 v 相邻,而且 v 不在路径 S -> T 上,则可以把该路径变成 v -> S -> T,然后 v 成为新的 S。从 S 和 T 分别向两头扩展,直到无法继续扩展为止,即所有与 S 或 T 相邻的节点都在路径 S -> T 上
  • 若 S 与 T 相邻,则路径 S -> T 形成了一个回路
  • 若 S 与 T 不相邻,可以构造出来一个回路。设路径 S -> T 上有 k+2 个节点,依次为 S, v1, v2, …, vk, T。可以证明存在节点 vi(i属于[1, k]),满足 vi 与 T 相邻,且 vi+1 与 S 相邻,找到这个节点 vi,把原路径变成 S -> vi -> T -> vi+1 -> S,即形成了一个回路.
  • 到此为止,已经构造出来了一个没有重复节点的的回路,如果其长度为 N,则哈密顿回路就找到了。如果回路的长度小于 N,由于整个图是连通的,所以在该回路上,一定存在一点与回路之外的点相邻。那么从该点处把回路断开,就变回了一条路径,同时还可以将与之相邻的点加入路径。再按照步骤 1 的方法尽量扩展路径,则一定有新的节点被加进来,接着回到路径 2

源程序

//#include <bits/stdc++.h>	//poj中不能使用万能头
#include <cstdio>
#include <iostream>
#include <cstdlib>
#include <algorithm>
#include <cstring>
#define MAXN 405
using namespace std;
int m,n,path[MAXN];
bool g[MAXN][MAXN],used[MAXN];
void Reverse(int a[],int s,int t)
{
	int tmp;
	while(s<t){
		/*tmp=a[s];
		a[s]=t;
		a[t]=s;*/
		swap(a[s],a[t]);
		s++;
		t--;
	}
}
void Hamilton()
{
	int s=1,t;	//初始化s,t 
	for(int i=1;i<=n;i++)
		if(g[s][i]){
			t=i;
			break;
		}
	used[s]=used[t]=true;
	path[0]=s,path[1]=t;
	int cnt=2;
	while(1){
		while(1){	//t向外扩展 
			bool flag=true;
			for(int i=1;i<=n;i++){
				if(g[t][i]&&!used[i]){
					path[cnt++]=i;
					used[i]=true;
					t=i;
					flag=false;
					break; 
				}
			}
			if(flag)break;
		}
		Reverse(path,0,cnt-1);	//翻转数组 
		swap(s,t);	//交换s,t
		while(1){	//t向外扩展 
			bool flag=true;
			for(int i=1;i<=n;i++){
				if(g[t][i]&&!used[i]){
					path[cnt++]=i;
					used[i]=true;
					t=i;
					flag=false;
					break; 
				}
			}
			if(flag)break;
		} 
		if(!g[s][t]){	//s与t不相邻 
			int i;		//取path[i]中与t相邻且,path[i+1]与s相邻 
			for(i=1;i<cnt-2;i++)
				if(g[path[i]][t]&&g[s][path[i+1]]) 
					break;
			i++;
			t=path[i];
			Reverse(path,i,cnt-1);	//将path[i+1]到t部分倒置 
		}	//此时s和t相连
		if(cnt==n)	return ;	//当前序列包含n个元素,算法结束 
		else{	//当前序列元素小于n,寻找path[i],使得path[i]与path[]外一点相连 
			int i,j;
			for(j=1;j<=n;j++){
				if(used[j])continue;
				for(i=1;i<cnt-2;i++)
					if(g[path[i]][j])
						break;
				if(g[path[i]][j])break;
			}
			s=path[i-1],t=j;
			Reverse(path,0,i-1);
			Reverse(path,i,cnt-1);
			path[cnt++]=j;
			used[j]=true; 
		} 
	}
}
int main()
{
	while(scanf("%d%d",&n,&m)!=EOF&&(n||m)){
		memset(g,true,sizeof(g));	//初始化 
		memset(used,false,sizeof(used));
		memset(path,0,sizeof(path));
		n*=2;
		for(int i=0;i<=n;i++)g[i][i]=false;
		for(int i=1;i<=m;i++){
			int x,y;
			scanf("%d%d",&x,&y);
			g[x][y]=g[y][x]=false;
		}
		Hamilton();
		for(int i=0;i<n;i++)printf("%d ",path[i]);
		printf("\n");
	}
}
 
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值