【POJ】3648 Wedding 2-sat

传送门:【POJ】3648 Wedding


题目分析:比较赤果果的一道2-sat。

对于编号为i的夫妻,i<<1为妻子,i<<1|1为丈夫。

如果有两个人x,y有奸情,则建边:< x , y ^ 1 > , < y , x ^ 1 >

因为新郎必须坐在新娘的对面,所以建边< 0 , 1 >

最后强连通加逆拓扑排序后,颜色为1的是坐在新郎那一边的(因为那边的所有人都不会有奸情)。

颜色为2的就是坐在新娘这边的(不晦气仅仅指新娘对面不能有通奸的,对于新娘这边没有要求)


代码如下:


#include <cstdio>
#include <cstring>
#include <algorithm>
using namespace std ;

#define REP( i , a , b ) for ( int i = ( a ) ; i <  ( b ) ; ++ i )
#define FOR( i , a , b ) for ( int i = ( a ) ; i <= ( b ) ; ++ i )
#define REV( i , a , b ) for ( int i = ( a ) ; i >= ( b ) ; -- i )
#define CLR( a , x ) memset ( a , x , sizeof a )
#define CPY( a , x ) memcpy ( a , x , sizeof a )

const int MAXN = 60 ;
const int MAXE = 10000 ;

struct Edge {
	int v ;
	Edge* next ;
} E[MAXE] , *H[MAXN] , *cur , t_E[MAXE] , *t_H[MAXN] , *t_cur ;

int dfn[MAXN] , low[MAXN] , scc[MAXN] , scc_cnt ;
int S[MAXN] , top , dfs_clock ;
int n , m ;
int in[MAXN] ;
int Q[MAXN] , head , tail ;
int color[MAXN] ;
int opp[MAXN] ;

void init () {
	cur = E ;
	top = scc_cnt = dfs_clock = 0 ;
	CLR ( H , 0 ) ;
	CLR ( dfn , 0 ) ;
	CLR ( scc , 0 ) ;
}

void t_init () {
	t_cur = t_E ;
	CLR ( t_H , 0 ) ;
	CLR ( in , 0 ) ;
}

void addedge ( int u , int v ) {
	cur -> v = v ;
	cur -> next = H[u] ;
	H[u] = cur ++ ;
}

void t_addedge ( int u , int v ) {
	t_cur -> v = v ;
	t_cur -> next = t_H[u] ;
	t_H[u] = t_cur ++ ;
}

void tarjan ( int u ) {
	dfn[u] = low[u] = ++ dfs_clock ;
	S[top ++] = u ;
	for ( Edge* e = H[u] ; e ; e = e -> next ) {
		int v = e -> v ;
		if ( !dfn[v] ) {
			tarjan ( v ) ;
			low[u] = min ( low[u] , low[v] ) ;
		} else if ( !scc[v] ) low[u] = min ( low[u] , dfn[v] ) ;
	}
	if ( low[u] == dfn[u] ) {
		++ scc_cnt ;
		do {
			scc[S[-- top]] = scc_cnt ;
		} while ( u != S[top] ) ;
	}
}

int ok () {
	REP ( i , 0 , n << 1 ) if ( !dfn[i] ) tarjan ( i ) ;
	REP ( i , 0 , n ) if ( scc[i << 1] == scc[i << 1 | 1] ) return 0 ;
	REP ( i , 0 , n ) {
		opp[scc[i << 1]] = scc[i << 1 | 1] ;
		opp[scc[i << 1 | 1]] = scc[i << 1] ;
	}
	return 1 ;
}

void topo () {
	head = tail = 0 ;
	CLR ( color , 0 ) ;
	FOR ( i , 1 , scc_cnt ) if ( !in[i] ) Q[tail ++] = i ;
	while ( head != tail ) {
		int u = Q[head ++] ;
		if ( !color[u] ) {
			color[u] = 1 ;
			color[opp[u]] = 2 ;
			for ( Edge* e = t_H[u] ; e ; e = e -> next )
				if ( -- in[e -> v] == 0 )
					Q[tail ++] = e -> v ;
		}
	}
}

void solve () {
	int u , v , ui , vi ;
	char c1 , c2 ;
	init () ;
	addedge ( 0 , 1 ) ;
	while ( m -- ) {
		scanf ( "%d%c%d%c" , &u , &c1 , &v , &c2 ) ;
		ui = c1 == 'w' ? 0 : 1 ;
		vi = c2 == 'w' ? 0 : 1 ;
		addedge ( ( u << 1 ) ^ ui , ( v << 1 | 1 ) ^ vi ) ;
		addedge ( ( v << 1 ) ^ vi , ( u << 1 | 1 ) ^ ui ) ;
	}
	if ( ok () ) {
		t_init () ;
		REP ( i , 0 , n << 1 )
			for ( Edge* e = H[i] ; e ; e = e -> next )
				if ( scc[e -> v] != scc[i] ) {
					t_addedge ( scc[e -> v] , scc[i] ) ;
					++ in[scc[i]] ;
				}
		topo () ;
		REP ( i , 1 , n )
			printf ( "%d%c%c" , i , color[scc[i << 1]] == 2 ? 'w' : 'h' , i < n - 1 ? ' ' : '\n' ) ;
	} else printf ( "bad luck\n" ) ;
}
	
int main () {
	while ( ~scanf ( "%d%d" , &n , &m ) && ( n || m ) ) solve () ;
	return 0 ;
}


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值