poj 2438 构造哈密顿圈(孩子只想和喜欢的相邻)

题意:有2n个孩子,现在要让他们坐成一个圈。输入为一系列二元组(a,b),表示孩子a不要和孩子b相邻。已知一个孩子最多讨厌n-1个孩子。问符合要求能否将孩子排列成一个圈,如果能,输出其中一种方案。

思路:以孩子互相喜欢连边构图。题意即求此图的一个哈密顿圈。由哈密顿图的dirac充分条件可知,此图是哈密顿图,所以肯定能够将孩子排成圈。找圈分4步:

1、首先任意选取一个初始顶点s,然后找一个与其相邻的点t;

2、反复扩展t,以找到一条路径;t保存为刚刚扩展到的点;

3、若st之间有边相连,即找到一个圈,待用;否则,将路径改造成圈:在st路径中找一点i(i必存在),使其满足s与i相邻并且i-1与t相邻。则s...i-1,t...i,s为一个圈,注意需要逆转数组的一部分。

4、如果所有点找到,输出;否则,找到一个没有在当前圈中的点i,找到其在属于圈中的相邻点j。变换使得j-1为新的起点而i为新的终点(需要逆转数组中的两部分)。返回步骤2继续扩展路径。


因为求解过程中要翻转数组,自己写当然可以,但是用stl的reverse函数会简化编程。

无stl版本:

#include <stdio.h>
#include <string.h>
#define N 205*2
int g[N][N],used[N],res[N];
int n,m,len;
void init(){
	int i,j;
	memset(g,0,sizeof(g));
	for(i = 1;i<=n;i++)
		for(j = 1;j<=n;j++)
			if(i!=j)
				g[i][j] = 1;
	memset(used,0,sizeof(used));
}
void reverse(int x,int y){
	int i,mid = (x+y)/2;
	for(i = x;i<=mid;i++){
		int temp = res[i];
		res[i] = res[y-i+x];
		res[y-i+x] = temp;
	}
}
int now(){
	int i,res=0;
	for(i = 1;i<=n;i++)
		res += used[i];
	return res==n;
}
void find_halmiton(){
	int s=1,t,i,j;
	for(i = 1;i<=n;i++)
		if(g[s][i])
			break;
	t = i;
	used[s] = used[t] = 1;
	res[0] = s;
	res[1] = t;
	len = 2;
	while(1){
		while(1){//反复扩展t,直到无法扩展
			for(i = 1;i<=n;i++)
				if(!used[i] && g[t][i]){
					res[len++] = i;
					used[i] = 1;
					t = i;
					break;
				}
			if(i>n)
				break;
		}
		if(!g[s][t]){//如果st不相邻,构造圈
			for(i = 2;i<len-1;i++)
				if(g[s][res[i]] && g[res[i-1]][t])
					break;
			reverse(i,len-1);
		}
		if(now())//如果所有点都找到
			break;
		for(i = 1;i<=n;i++){//找一个没有在当前圈中的顶点
			if(used[i])
				continue;
			for(j = 1;j<len;j++)//找到它在属于当前圈中的相邻点
				if(g[i][res[j]])
					break;
			if(j < len)
				break;
		}
		s = res[j-1];
		t = i;
		reverse(0,j-1);
		reverse(j,len-1);
		res[len++] = i;
		used[i] = 1;
	}
}
int main(){
	freopen("a.txt","r",stdin);
	while(scanf("%d %d",&n,&m) && (n+m)){
		int i,j,a,b;
		n<<=1;
		init();
		for(i = 0;i<m;i++){
			scanf("%d %d",&a,&b);
			g[a][b] = g[b][a] = 0;
		}
		find_halmiton();
		for(i = 0;i<len;i++)
			printf("%d ",res[i]);
		putchar('\n');
	}
	return 0;
}

用reverse版本:

#include <cstdio>
#include <cstring>
#include <cstdlib>
#include <algorithm>
using namespace std;
#define N 405
#define INF 0x3fffffff
#define clc(s,t) memset(s,t,sizeof(s))
int g[N][N],used[N],res[N],n,m,len;
int finish(){
	for(int i = 1;i<=n;i++)
		if(!used[i])
			return 0;
	return 1;	
}
void solve(){
	int i,j,s,t;
	s = t = 1;
	used[s] = 1;
	res[++len] = s;
	while(1){
		while(1){
			for(i = 1;i<=n;i++)
				if(!used[i] && g[t][i])
					break;
			if(i>n)
				break;
			used[i] = 1;
			res[++len] = i;
			t = i;
		}
		if(!g[s][t]){
			for(i = 2;i<=len;i++)
				if(g[res[i-1]][t] && g[res[i]][res[0]])
					break;
			reverse(res+i,res+len+1);
		}
		if(finish())
			break;
		for(i = 1;i<=n;i++){
			if(!used[i]){
				for(j = 0;j<=len;j++){
					if(g[i][res[j]])
						break;
				}	
				if(j<=len)
					break;
			}	
		}
		t = res[j];
		s = res[j-1];
		reverse(res,res+j);
		reverse(res+j,res+len+1);
		
	}	
}
int main(){
	freopen("a.txt","r",stdin);
	while(scanf("%d %d",&n,&m) && (n+m)){
		int i,j,a,b;
		clc(used,0);
		n <<= 1;
		len = -1;
		for(i = 1;i<=n;i++)
			for(j = 1;j<=n;j++)
				g[i][j] = 1;
		for(i = 1;i<=m;i++){
			scanf("%d %d",&a,&b);
			g[a][b] = g[b][a] = 0;	
		}
		solve();
		for(i = 0;i<n;i++)
			printf("%d ",res[i]);
		putchar('\n');
	}
	return 0;	
}


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值