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;
    }
    
  • 相关阅读:
    蓝牙打印机的连接方法
    CE不能开机的可能情况
    手机性能指标的建议
    WINCE中使用键盘钩子的注意事项
    Wave接口开发注意事项
    解决唤醒屏不亮的问题之总结
    WM系统有用的注册表(研发人员使用)
    快速求解两个时间之间的天数
    测试SqlBulkCopy类批量插入数据
    Xml与DataTable相互转换方法
  • 原文地址:https://www.cnblogs.com/little-uu/p/13953610.html
Copyright © 2011-2022 走看看