zoukankan      html  css  js  c++  java
  • 闲谈2-sat问题

    问题简介

    在计算机科学中,布尔可满足性问题(有时称为命题可满足性问题,缩写为SATISFIABILITY或SAT)是确定是否存在满足给定布尔公式的解释的问题。换句话说,它询问给定布尔公式的变量是否可以一致地用值TRUE或FALSE替换,公式计算结果为TRUE。如果是这种情况,公式称为可满足。另一方面,如果不存在这样的赋值,则对于所有可能的变量赋值,公式表示的函数为FALSE,并且公式不可满足。例如,公式“a AND NOT b”是可以满足的,因为可以找到值a = TRUE且b = FALSE,这使得(a AND NOT b)= TRUE。相反,“a AND NOT a”是不可满足的。——百度百科

    在每个限制中牵扯到的最多的元素假设是(k),我们就称为(k-sat)问题。可以证明当(k ge 3)时为(np)完全问题。下面只讨论(k=2)的情况。

    引入

    从一个最简单的(2-sat)问题开始今天的讨论

    有n对夫妻被邀请参加一个聚会,因为场地的问题,每对夫妻中只有1人可以列席。在2n 个人中,某些人之间有着很大的矛盾(当然夫妻之间是没有矛盾的),有矛盾的2个人是不会同时出现在聚会上的。有没有可能会有n 个人同时列席?——hdu3062

    对于每对夫妻,我们可以将丈夫去看作(1),妻子去看作(0)。要求给每对夫妻确定一个值,使得满足这(m)个限制。

    这就是最简单的(2-sat)模型。

    构图

    先观察这些限制,假设A中的丈夫不能和B中的妻子一起去(???)。那么也就是说,A中的丈夫只能和B中的丈夫一起去。也就是说,如果A中的丈夫去,那么B中的丈夫也必须去。同理,如果B中的妻子去,那么A中的妻子也必须去
    我们就从A中的丈夫向B中的丈夫连一条边,从B中的妻子向A中的妻子连一条边,表示这些"必须"的条件。

    显然我们就可以得到这样一个结论:如果一个点被选了,那么这个点所能到达的所有点也必须被选择。如果一个点没选,那么所有可以到达这个点的点都不能选。出现矛盾当且仅当某对夫妻的丈夫和妻子都必须去。

    算法

    暴力解法

    我们枚举每一个人选或者不选,然后去查看是否会出现矛盾。
    如果一对夫妻中两个人不论选择谁都会出现矛盾。那么问题必定无解。

    优化

    继续推一些结论。
    对于同一个强连通分量里的点,如果选择其中一个,那么这个强连通分量中的点必须全部选择。这样我们可以先(tarjan)缩点。如果一对夫妻出现在了同一个强连通分量里面肯定无解。否则一定有解。
    找可行解可以先拓扑排序一下,然后自底向上的顺序推导。

    证明

    观察我们的构图方式可以发现构出来的图有对称性
    啥是对称性??

    如图。我们观察其中的一组点,比如(c)

    发现可以到达(c_0)的点是(a_0),(c_0)可以到达的点是(d_0),而可以到达(c_1)的点是(d_1),(c_1)可以到达的点是(a_1)。刚好对称。也就是说我们只要保证(c_0)可以到达的不会矛盾,那么就可以保证可以到达(c_1)的点不会矛盾。我们如果选择了(c_0),那么就一定选择(d_0),这时把(c_1)(d_1)都删去。按照这种思路,我们每次找到一个未确定的点(x),使得(x)到达的点中没有(x')(即x的对立点)。
    这样我们一定可以找到一组解。而无解的情况也就是找不到这种点的时候。即一对点存在于同一个强连通分量中。
    然后思考求解的过程,因为要使得“(x)到达的点中没有(x')(即x的对立点)。”
    所以可以拓扑排序。然后就自底向上的找答案。
    有一种更加简单的找可行解的方法。

    注意到(Tarjan)算法的本质是一次(dfs),他在回溯时会优先取出有向图"底部"的(scc)进行标记。故(Tarjan)算法得到的(scc)编号本身就已经满足缩点后的有向无环图中"自底向上"的拓扑序。——《算法竞赛进阶指南》

    得出结论:直接比较缩点之后编号的大小就可以得出答案,较小的为1,较大的为0。

    资料

    我讲的不好??
    没关系,再看些资料吧。
    https://blog.csdn.net/qq_24451605/article/details/47126143
    由对称性解2-sat问题

    代码

    /*
    * @Author: wxyww
    * @Date:   2019-04-28 18:50:21
    * @Last Modified time: 2019-04-28 19:59:02
    */
    #include<cstdio>
    #include<iostream>
    #include<cstdlib>
    #include<cstring>
    #include<algorithm>
    #include<queue>
    #include<vector>
    #include<ctime>
    using namespace std;
    typedef long long ll;
    const int N = 2010;
    ll read() {
    	ll x=0,f=1;char c=getchar();
    	while(c<'0'||c>'9') {
    		if(c=='-') f=-1;
    		c=getchar();
    	}
    	while(c>='0'&&c<='9') {
    		x=x*10+c-'0';
    		c=getchar();
    	}
    	return x*f;
    }
    struct node{
    	int v,nxt;
    }e[1000000];
    int n,m,ejs,head[N];
    void add(int u,int v) {
    	e[++ejs].v = v;e[ejs].nxt = head[u];head[u] = ejs;
    }
    int vis[N],tot,dfn[N],low[N],sta[N],coljs,top,col[N];
    void tarjan(int u) {
        dfn[u] = low[u] = ++tot;
        sta[++top] = u;vis[u] = 1;
        for(int i = head[u];i;i = e[i].nxt) {
            int v = e[i].v;
            if(!dfn[v]) {
                tarjan(v);
                low[u] = min(low[u],low[v]);
            }
            else if(vis[v]) low[u] = min(low[u],low[v]);
        }
        if(low[u] == dfn[u]) {
            ++coljs;
            do {
                int x = sta[top--];
                col[x] = coljs;
                vis[x] = 0;
            }while(sta[top + 1] != u);
        }
    }
    int main() {	
    	while(~scanf("%d%d",&n,&m)) {
       memset(head,0,sizeof(head));
       ejs = 0;
       coljs = 0;
       memset(dfn,0,sizeof(dfn));memset(low,0,sizeof(low));
       tot = 0;top = 0;
    	
    	for(int i = 1;i <= m;++i) {
    		int x = read() + 1,y = read() + 1;
    		int c1 = read(),c2 = read();
    		if(c1) {
    			if(c2) add(x + n, y),add(y + n,x);
    			else add(x + n,y + n),add(y,x);
    		}
    		else {
    			if(c2) add(x, y),add(y + n,x + n);
    			else add(x,y + n),add(y,x + n);
    		}
    	}
    
    	for(int i = 1;i <= n + n;++i) if(!dfn[i]) tarjan(i);
    	int bz = 0;
    	for(int i = 1;i <= n;++i) {
    		if(col[i] == col[i + n]) {
    			bz = 1;break;
    		}
    	}
    	puts(bz ? "NO" : "YES");
    }
    	return 0;
    }
    
  • 相关阅读:

    【工作】---前后端联调
    【react】---Immutable的基本使用
    【react】传值
    【原生】 HTML DOM 事件,各种事件类型、事件种类
    两台笔记本电脑之间实现屏幕扩展
    【看图学习后台管理系统】
    【bug】在react开发中,使用link 跳转中,无法点击跳转的问题
    【前端工程师】 web 安全问题
    【前端工程师】 性能和效率 优化的问题
  • 原文地址:https://www.cnblogs.com/wxyww/p/2-sat.html
Copyright © 2011-2022 走看看