zoukankan      html  css  js  c++  java
  • 2-SAT--UVA11294 Wedding

    problem

    给定(n)个布尔型变量(x_i)以及他们之间的逻辑关系例如((x_jlor x_i) land (lnot x_plor x_q)……),求出一组合法解

    solution

    显然每个点都有两种状态:真和假。这样我们可以把(n)个点拆分成(2n)个点,(x)(lnot x).

    对于每一对关系((ilor j)),我们就可以( eg iLongrightarrow j)(lnot jLongrightarrow i)这样连出两条边

    那么现在变量(x)有四种连边的情况:

    • (x)(lnot x),此时之间没有边,随便取

    • (xLongrightarrow lnot x),此时(x)为假

    • (lnot xLongrightarrow x),此时(x)为真

    • (xLongrightarrow lnot x)(lnot xLongrightarrow x)(x)又为真又为假,无解

    那么如何求解的情况呢??看看上面的第四种情况就可以联想到(tarjan)s缩点,如果(x)(lnot x)在同一个强联通分量中就无解

    如果(x)所在的强连通分量的拓扑序在(lnot x)所在的强连通分量之后(x)为真(并不会证明)。

    另外拓扑序其实就是(tarjan)染色的逆序,这样我们只需要判断(x)的颜色(<)(lnot x)的颜色就ok了

    例题

    考虑对每个夫妇建两个点 (x),(lnot x)(x)表示妻子与新郎同侧,(lnot x)则表示丈夫与新郎同侧。

    那么对于一对关系((a,b))(a)(lnot b),(b)(lnot a)各连一条边,表示若(a)与新郎同侧,那么(b)不能与新郎同侧,也就是(lnot b)一定要与新郎同侧,反之亦然。

    输出时反过来,即若与新郎同侧的是妻子,那么与新娘同侧的(也就是需要输出的人)就是丈夫

    (快读这东西就离谱)

    code:

    #include <iostream>
    #include <cstdio>
    #include <algorithm>
    #include <cstring>
    using namespace std;
    int read(){
    	int x = 1,a = 0;char ch = getchar();
    	while (ch < '0'||ch > '9'){if (ch == '-') x = -1;ch = getchar();}
    	while (ch >= '0'&&ch <= '9'){a = a*10+ch-'0';ch = getchar();}
    	return x*a;
    }
    
    const int maxn = 4e5+10;
    
    int n,m;
    
    int getnum(int x){
    	if (x > n) return x - n;
    	return x + n;
    }
    
    struct node{
    	int to,nxt;
    }ed[maxn*2];
    
    int head[maxn*2],tot;
    
    void add(int u,int to){
    	ed[++tot].to = to;
    	ed[tot].nxt = head[u];
    	head[u] = tot;
    }
    
    int col[maxn*2],top,dfn[maxn*2],low[maxn*2],color,cnt,st[maxn*2];
    
    void tarjan(int x){
    	dfn[x] = low[x] = ++cnt;
    	st[++top] = x;
    	for (int i = head[x];i;i = ed[i].nxt){
    		int to = ed[i].to;
    		if (!dfn[to]){
    			tarjan(to);
    			low[x] = min(low[x],low[to]);
    		}
    		else if (!col[to]) low[x] = min(low[x],dfn[to]);
    	}
    	if (dfn[x] == low[x]){
    		++color;
    		int y;
    		while (y = st[top--]){
    			col[y] = color;
    			if (x == y) break;
    		}
    	}
    }
    
    bool flag;
    
    void init(){
    	memset(head,0,sizeof(head));
    	memset(ed,0,sizeof(ed));
    	memset(col,0,sizeof(st));
    	memset(dfn,0,sizeof(dfn));
    	memset(low,0,sizeof(low));
    	memset(st,0,sizeof(st));
    	color = cnt = tot = top = 0;
    	flag = true;
    }
    
    int main(){
    	while (~scanf ("%d%d",&n,&m)&&(n+m) != 0){
    		init();
    		for (int i = 1;i <= m;i++){
    			char op;
    			int u ;scanf("%d",&u);scanf ("%c",&op);u++;
    			if (op == 'h') u+=n;
    			int v ;scanf("%d",&v);scanf ("%c",&op);v++;
    			if (op == 'h') v+=n;
    			add(u,getnum(v));
    			add(v,getnum(u));
    		}
    		add(1,1+n);
    		for (int i = 1;i <= 2*n;i++){
    			if (!dfn[i]) tarjan(i);
    		}
    		for (int i = 1;i <= n&&flag;i++){
    			if (col[i] == col[i+n]) flag = false;
    		}
    		if (!flag){printf("bad luck
    ");continue;}
    		for (int i = 2;i <= n;i++){
    			if (col[i] > col[i+n]) printf("%dw ",i-1);
    			else printf("%dh ",i-1);
    		}
    		puts("");
    	}
    	return 0;
    }
    
  • 相关阅读:
    es5预览本地文件、es6练习代码演示案例
    Java实现 LeetCode 838 推多米诺(暴力模拟)
    Java实现 LeetCode 838 推多米诺(暴力模拟)
    Java实现 LeetCode 838 推多米诺(暴力模拟)
    Java实现 LeetCode 837 新21点(DP)
    Java实现 LeetCode 837 新21点(DP)
    Java实现 LeetCode 837 新21点(DP)
    Java实现 LeetCode 836 矩形重叠(暴力)
    Subversion under Linux [Reprint]
    Subversion how[Reprint]
  • 原文地址:https://www.cnblogs.com/little-uu/p/13953610.html
Copyright © 2011-2022 走看看