http://poj.org/problem?id=3648
题意:有N-1对夫妻去参加0号夫妻的婚礼,0号新娘和0号新郎分做桌子的两边,现在已知同一对夫妻不能做在同 一边,而且给出M对“通奸”关系,要求这M对人不能有任意一对人同时做在新郎这一边,问是否有解, 有解则输出解。
思路:2-Sat问题。建图的方法是:0号夫妻中,新郎必须在选,也就是说不能选新娘,我们就可以在新娘和新郎之间加一条边,说明要是选了新娘就必要选新郎,这样就会导致矛盾,同时也就间接地规定不能选新娘了。对于其他的通奸关系(i,j),我们可以建图:(i , j') 和 ( j , i' )。 最后用2-Sat求解即可。
代码:
#include<stdio.h> #include<string.h> const int MAXN = 70*2 ; int N ,M ; int Gv[MAXN*MAXN] , Gnext[MAXN*MAXN] , Gr[MAXN] , Gc ; int dfn[MAXN] , low[MAXN] , stack[MAXN] , belong[MAXN] , cf[MAXN]; bool in[MAXN] ; int top , idx , Bcnt ; int gv[MAXN*MAXN] ,gnext[MAXN*MAXN] , gr[MAXN] , gc ; int degree[MAXN] ,que[MAXN] ,front ,rear , col[MAXN]; void init(){ memset(Gr, -1, sizeof(Gr)); Gc = 0 ; } void add(int a, int b){ Gv[Gc] = b ; Gnext[Gc] = Gr[a] ; Gr[a] = Gc ++ ; } void tarjin(int u){ int v ; low[u] = dfn[u] = ++idx ; stack[++top] = u ; in[u] = 1 ; for(int i=Gr[u] ;i!=-1;i=Gnext[i]){ v = Gv[i] ; if( !dfn[v] ){ tarjin(v); if(low[v] < low[u]) low[u] = low[v] ; } else if( in[v] && dfn[v] < low[u]) low[u] = dfn[v] ; } if( dfn[u] == low[u] ){ Bcnt ++ ; do{ v = stack[top--] ; in[v] = 0 ; belong[v] = Bcnt ; }while(u != v) ; } } void add2(int a, int b){ gv[gc] = b ; gnext[gc] = gr[a] ; gr[a] = gc++ ; } void solve(){ memset(gr , -1, sizeof(gr)); gc = 0 ; memset(degree , 0 ,sizeof( degree) ) ; for(int i=1;i<=2*N;i++){ for(int j=Gr[i] ;j!=-1;j=Gnext[j]){ int v = Gv[j] ; int fa = belong[i] ; int fb = belong[v] ; if( fa != fb){ add2(fb ,fa); degree[fa] ++ ; } } } front = rear = 0 ; for(int i=1;i<=Bcnt;i++){ if( degree[i] == 0) que[rear++] = i ; } memset(col , 0 ,sizeof(col)); while(front != rear){ int v = que[front++] ; if( col[v] == 0 ){ col[v] = 1 ; col[ cf[v] ] = -1 ; } for(int i=gr[v];i!=-1;i=gnext[i]){ int u = gv[i] ; if( --degree[u] == 0) que[rear++] = u ; } } bool first = 1 ; for(int i=1;i<=N;i++){ if(first == 0) printf(" "); first = 0 ; if( col[ belong[2*i-1] ] == -1 ){ printf("%dw",i); } else printf("%dh",i); } printf("\n"); } int main(){ int a, b ,c , d ; char ch1, ch2 ; while(scanf("%d%d",&N,&M) == 2){ if(0==N && M==0) break ; init() ; for(int i=1;i<=M;i++){ scanf("%d%c %d%c",&a,&ch1,&b,&ch2); if(a == b) continue ; if( ch1=='h' ){ c = 2 * a -1 ; a = 2 * a ; } else{ c = 2 * a ; a = 2 * a - 1 ; } if( ch2=='h' ){ d = 2 * b - 1 ; b = 2 * b ; } else{ d = 2 * b; b = 2 * b - 1 ; } if(a==0){ if(ch1=='h'){ add(b,d); } else continue ; } else if(b == 0){ if(ch2=='h'){ add(a,c); } else continue ; } else{ add(a,d); add(b,c); } } N-- ; top = idx = Bcnt = 0 ; memset(dfn , 0 ,sizeof(dfn)); memset(in , 0 ,sizeof(in)); for(int i=1;i<=2*N;i++){ if( !dfn[i] ) tarjin(i); } bool ok = 1 ; for(int i=1;i<=N;i++){ if( belong[2*i-1] == belong[2*i] ){ ok = 0 ; break ; } cf[ belong[2*i-1] ] = belong[2*i] ; cf[ belong[2*i] ] = belong[2*i-1] ; } if(!ok){ printf("bad luck\n"); continue ; } solve() ; } return 0 ; }