• 2


    【定义】:

    (SAT) 是适定性 ((Satisfiability)) 问题的简称 。一般形式为 (k) -适定性问题,简称 (k-SAT)

    这个描述来自于百度,没什么意义,主要是下面的概念

    【问题描述】 :

    (k-SAT) 的问题描述如下 :

    一般有 (x_1 , x_2 , x_3 …… x_n)(n) 个变量和 (m) 个关于 (n) 个变量之间的限制
    每一个限制都是一个二元方程组, (x_1 igoplus x_2 igoplus x_3 igoplus …… igoplus x_v = 0 / 1) ,其中 (igoplus) 为一个二元 (bool) 形运算。

    可以证明 (k > 2) 时,属于 (NP) 完全问题,只能用暴力解决, 本文讨论 (2-SAT) 问题。(可以认为笔者很是 (fw) ),连暴力也不知道怎么打。


    同样的,每一个变量大概就是这么一个图。
    对于我来说, (2-SAT) 问题就是由 (m) 个约束条件形成的一张图。

    【解决】 :

    $2 - SAT $ 问题可以和图论相联系起来。 那么考虑建图

    以一个例题作为简要讨论。

    【模板】

    (description)】 :
    见题面 。
    (solution)】:
    我们以样例分析入手 。

    然后我们就可以建立出样例三个变量的图形了,有关图好看与否这种弱智讨论还是放一下吧

    虽然这一个题目只有一个限制,但是说起来很好说 , 但是也是可以推导出一些东西的。

    这一个限制是要么(x_1 = 1) ,要么(x_3= 0) ,这两个之间显然是必然有一个成立的。 或者说,至少有一个成立。对于(x_3 eq 0),这个问题,我们是没有办法说是当(x_1 = 1) 时,(x_3 eq 0) 一定成立。

    关于这个问题的证明,倒是没那么好证,但是对于举出一个反例是相当的简单。如果有两个此时再有一个限制 , 要么(x_3 = 0),要么(x_2 = 3) ,那么这时候,这个(x_3 = 0)亦然是可能出现的。

    所以我们也可以知道,关于上方的式子 :

    [x_1 eq 1 o x_3 = 0 ]

    [x_3 eq 0 o x_1 = 1 ]

    [x_1 = 1 ot o x_3 eq 0 ]

    [x_3= 0 ot o x_1 eq 1 ]

    当然,是个人都会知道( o) 是推导的意思。 那么同样的,我们将推导和被推导出建边,~

    同样的,点我们是需要建立两个的,分别代表(true) 和$false $ , 然后就没什么可说的了。

    但是我们这个点是 (true) 的时候好记,但是当这一个是 (false) 的时候, 我们自然不能在(1 o n) 这些节点建立了,所以我们只能重新建立节点 ,那么我们这个节点数就往后顺延(n) , 举个例子,就是 (1) 的节点的对立面我们就新建结点 (n+1) 表示他的对立面。同样的,不同的题目用不同的方法,这个题自然是这么建好一些 的 。
    由于这个题给定的就是真或者是假,那么我们就需要考虑一下

    我们首先假设两个事件分别为 (A) 和事件 (B) ,
    (1.) 那么当 (A)(and) (B) 真的时候 ,那么我们就可以推导出 (A) 为真的时候,可以推导出 (B) 也是真的 , (B) 为假的时候 , (A) 也是为假
    (2.)(A)(and) (B) 假的时候, 那么我们就可以推导出 (A) 为真的时候, (B) 为假,当 (B) 为真的时候, (A) 也是假
    (3.)(A)(and) (B) 假的时候, 我们就可以推导出 (A) 为假的时候 , (B) 为假。
    同理,当 (B) 为真的时候 ,(A) 为假 。
    (4.)(A) 真, (B) 真的时候, 我们同样可以推导出 (A) 为假的时候 , (B) 为真 ,
    (B) 为假的时候 , (A) 为真

    那么我们就可以得到建图的代码 :

    	for(int i = 1 ; i <= m ; i++ ) 
    	{
    		int x = read() , a = read() , y = read() , b = read() ; 
    		if(a && b ) add(x , y + n) , add(y , x + n) ;   
    		else if(!a && b) add(x + n , y + n) , add(y , x) ; 	
    		else if(a && !b) add(x , y) , add(y + n , x + n) ; 
    		else if(!a && !b) add(x + n , y) , add(y + n , x ) ; 
    	}
    

    然后我们考虑什么时候会没有解,那必然是其对立面反而推出了自己,也就是节点 (u) 能够 被 (u + n) 这一个节点推导出来,这叫数学的反证法,如果不知道其具体,百度是个好东西,那么也就是说明, (u) 这一个节点和 (u+n) 这一个节点在一个强连通分量里。

    同样的输出方案也就是强连通分量了,这里就不赘述强连通分量了。没有什么意义,若是不会的话,就看一下笔者曾经写过的一篇文章,(Tarjan) 算法

    直接上代码 (Code)

    /*
     by : Zmonarch
     知识点 :2 - SAT 模板 
    */
    #include <iostream>
    #include <cstring>
    #include <cstdio>
    #include <algorithm>
    #include <queue>
    #include <stack>
    #include <map>
    #include <set>
    #include <vector>
    #include <cmath>
    #define int long long
    const int kmaxn = 2e6 + 10 ;
    inline int read()
    {
    	int x = 0 , f = 1 ; char ch = getchar() ;
    	while(!isdigit(ch)) { if(ch == '-') f = - 1 ; ch = getchar() ; }
    	while( isdigit(ch)) { x = x * 10 + ch - '0' ; ch = getchar() ; }
    	return x * f ;
    }
    inline int Min(int a , int b)
    {
    	if(a < b) return a ; else return b ;
    }
    inline int Max(int a , int b)
    {
    	if(a > b) return a ; else return b ;
    }
    inline int Abs(int a)
    {
    	if(a < 0) return - a ; else return a ;
    }
    struct node 
    {
    	int nxt , u , v , val ; 
    } e[kmaxn << 1] ; 
    int n , m ; 
    int tot , h[kmaxn << 1] ; 
    void add(int u , int v ) 
    {
    	e[++tot].nxt = h[u] ; 
    	e[tot].u = u ; 
    	e[tot].v = v ; 
    	h[u] = tot ; 
    }
    int dfn[kmaxn] , low[kmaxn] , stack[kmaxn] , in[kmaxn] , scc[kmaxn] ; 
    int top , sum , cnt ;  
    void Tarjan(int u) 
    {
    	dfn[u] = low[u] = ++sum ; 
    	stack[++top] = u ; 
    	for(int i = h[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(!in[v]) low[u] = Min (low[u] , dfn[v]) ; 
    	}
    	if(low[u] == dfn[u]) 
    	{
    		in[u] = ++ cnt ; 
    		++scc[cnt] ; 
    		while(stack[top + 1] != u) 
    		{
    			in[stack[top]] = cnt ; 
    			++scc[cnt] ; 
    			top -- ; 
    		}
    	}
    }
    signed main()
    {
    	n = read() , m = read() ; 
    	for(int i = 1 ; i <= m ; i++ ) 
    	{
    		int x = read() , a = read() , y = read() , b = read() ; 
    		if(a && b ) add(x , y + n) , add(y , x + n) ;   
    		else if(!a && b) add(x + n , y + n) , add(y , x) ; 	
    		else if(a && !b) add(x , y) , add(y + n , x + n) ; 
    		else if(!a && !b) add(x + n , y) , add(y + n , x ) ; 
    	}
    	for(int i = 1 ; i <= n + n ; i++) //对立面也要搞
    	{
    		if(!dfn[i]) 
    		{
    			Tarjan(i) ;
    		}
    	} 
    	for(int i = 1 ; i <= n ; i++ )
    	{
    		if(in[i] == in[i + n]) 
    		{
    			printf("IMPOSSIBLE
    ") ;
    			return 0 ; 
    		}
    	}
    	
    	printf("POSSIBLE
    ") ; 
    	for(int i = 1 ; i <= n ; i++) 
    	{
    		printf("%d " , in[i] > in[i + n] ) ; 
    	}
    	return 0 ;
    }
    

    洛谷上的题就不再赘述了。

    附加题

    给定 (n) 个变量 ,为 (x_1 , x_2 , x_3 , x_4 , x_5 , …… x_{n-1} , x_n) ((forall x leq 100)) , (m) 个表达式 , (n , m leq 100)
    有关表达式的形式
    输入 (4) 个整数 , (x_i , opt , x_j , val)
    意为 $$x_i + or imes or div or - x_j leq val$$
    也就是加减乘除均可以。

    那么考虑 (2-SAT) , 我们以 (+) ,为例,为什么用 (+) ,显然,好算,好说 。
    我们给定一个式子 (x_1 + x_2 leq 5)

    [ egin{array}{c|cccc} % 第二行 Delta 值数组 Delta & xgeq 1 & xgeq 2 & xgeq 3 & xgeq 4 & xgeq5\ hline true & 0 & 0 & 0 & 0 &0\ false & 0 & 0 & 0 & 0 & 0\ end{array} % 第二行表格结束 ]

    如果我们选择以 (x) 的取值范围作为事件,那么我们就可以得到 (true) ,或者 (false) 了 ,就类似于上方的图,但是我并不打算用上方的图说明,笔者不怎么会用

    [x_1 + x_2 leq 5 $$ 在加上 $x_1 geq 2$ ,那么很显然就会得到 $x_2 leq 3$。显然是可以建立推导关系的。 >那么如果我选择$x_1 leq 1$ 的时候,我们发现 $x_2 leq 5$ ,我们可以得到一个等式的关系,考虑是否是可以推导出来呢?当 $x_1 leq 3$ ,我们发现 $x_2 leq 5$ ,仍然是上方的值,那就细去考虑,发现 我们 $x_2leq 5$的这一个取值并不是由 $x_1 leq ?$推导出来的,而是有$x_1 + x_2 leq 5$和$x_1$ 最小为 $1$ 得到的,显然,不符合我们 $2-SAT$ 的条件 。所以我们选择用 $geq$ 。 所以我们就可以通过这种方式建立 $2-SAT$ 的问题了。由于这种题目也是没有什么具体背景和数据或者说,这个题是 $yy$ 出来的。所以没有代码实现 。]

  • 相关阅读:
    【转载】python字符格式化
    【python】生产者消费者问题优化
    【python】pickle文件读写的insecure问题
    【工具使用】sublime设置换行符为unix风格
    【python】CGI部署问题解决
    【python】引用c的dll
    【多线程】Python进程,队列和锁相关的一些问题
    【Python】下载图片,标识之后转发出去
    【Python】PIL在window64位机制上引用异常问题解决
    【Python网页分析】httplib库的重定向处理
  • 原文地址:https://www.cnblogs.com/Zmonarch/p/14387974.html
Copyright © 2020-2023  润新知