poj 3648 Wedding

本文介绍了一种使用图论和深度优先搜索解决复杂关系匹配问题的方法。通过建立矛盾关系图并进行强连通分量分析,确保了宴会座次安排符合特定约束条件,即避免存在异常关系的人士被安排在同一侧。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

题意:

新郎新娘宴会,宴会上一共有 n 对夫妇,编号分别为 0 ~ n - 1,其中新郎新娘编号为 0,丈夫为 h,妻子为 w 。

宴会的长桌上,新郎新娘各坐一边,两边人数各为 n。在所有人中,存在 m 对异常关系,现在要求,新娘对面的那一边所有人之间不能存在异常关系,要求你求出新娘的这边的坐的人。这里的异常关系指偷情(男男、男女、女女)。

分析:

矛盾关系,有异常关系的两人不能同时存在,即 a && b == 0。(a 与 b 两个可以选择其中一人做新郎这一边,也可以选择都坐在新娘这一边)

需要注意的是,新郎必须在,因此必须在图中加一条 0 -> n 的边。(证明请找度娘,谢谢)

此外,有一点很要命,输入不要"%s%s",测试数据中存在无空格输入的现象,用“%d%c %d%c”吧,这个,我表示我画了一个小时陷在这个坑里,悲催呀。。。

#include<cstdio>
#include<cstring>
#include<algorithm>
#include<vector>
#include<queue>
using namespace std;
const int maxn = 11000;
const int inf = 1000000000;
vector<int>edge[maxn];
int n, m;
int tmpdfn, dfn[maxn], low[maxn], inst[maxn], belong[maxn], st[maxn], top, scnt;
int opp[maxn], in[maxn], color[maxn];
vector<int>arc[maxn];
queue<int>q;
void tarjan( int u )
{
	int i, v, t, size;
	low[u] = dfn[u] = tmpdfn++;
	st[top++] = u;
	inst[u] = 1;
	size = edge[u].size();
	for( i = 0; i < size; i++ )
	{
		v = edge[u][i];
		if( dfn[v] == -1 )
		{
			tarjan( v );
			low[u] = min( low[u], low[v] );
		}
		else if( inst[v] )low[u] = min( low[u], dfn[v] );
	}
	if( dfn[u] == low[u] )
	{
		do{ belong[t = st[--top]] = scnt; inst[t] = 0; }while( t != u );
		scnt++;
	}
}
bool SCC()
{
	int i;
	top = 0;
	tmpdfn = scnt = 1;
	memset( dfn, -1, sizeof(dfn) );
	memset( inst, 0, sizeof(inst) );
	for( i = 1; i <= n * 2; i++ )if( dfn[i] == -1 )tarjan( i );
	for( i = 1; i <= n; i++ )if( belong[i] == belong[i + n] )return false;
	return true;
}
void rebuild()
{
	memset( in, 0, sizeof(in) );
	int u, v, i, size;
	for( i = 1; i <= n * 2; i++ )arc[i].clear();
	for( u = 1; u <= n * 2; u++ )
	{
		size = edge[u].size();
		for( i = 0; i < size; i++ )
		{
			v = edge[u][i];
			if( belong[u] != belong[v] )
			{
				arc[belong[v]].push_back( belong[u] );
				in[belong[u]]++;
			}
		}
	}
}
void topusort()
{
	int i, j, u, v, size;
	while( !q.empty() )q.pop();
	memset( color, 0, sizeof(color) );
	for( i = 1; i < scnt; i++ )if( !in[i] )
		q.push( i );
	while( !q.empty() )
	{
		u = q.front();	q.pop();
		if( !color[u] )color[u] = 1, color[opp[u]] = 2;
		size = arc[u].size();
		for( i = 0; i < size; i++ )
		{	
			v = arc[u][i];
			in[v]--;
			if( !in[v] )q.push( v );
		}
	}
}
int main()
{
	int i, j, a, b, aa, bb;
	char s1, s2;
	while( ~scanf( "%d%d", &n, &m ), n + m )
	{
		for( i = 1; i <= n * 2; i++ )edge[i].clear();
		while( m-- )
		{
			scanf( "%d%c %d%c", &a, &s1, &b, &s2 );	// %s%s 输入有误,案例中输入无空格
			a++;	b++;
			aa = s1 == 'w' ? 0 : 1;
			bb = s2 == 'w' ? 0 : 1;
			edge[a + aa * n].push_back( b + (!bb) * n );
			edge[b + bb * n].push_back( a + (!aa) * n );
		}
		edge[1].push_back( 1 + n );
		if( SCC() )
		{
			for( i = 1; i <= n; i++ )
			{
				opp[belong[i]] = belong[i + n];
				opp[belong[i + n]] = belong[i];
			}
			rebuild();
			topusort();
			for( i = 2; i <= n; i++ )
				if( color[belong[i]] == color[belong[1]] )printf( "%dw ", i - 1 );
				else printf( "%dh ", i - 1 );
			puts("");
		}
		else
			puts( "bad luck" );
	}
	return 0;
}
//1h 2w 3w 4h 5w 6w 7h 8w 9h
 


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值