zoukankan      html  css  js  c++  java
  • 【图论】2-SAT 问题

    目录

    简介
    做法
    代码

    简介

    k-SAT(全称Satisfiability)问题,具体来说,给定 (n) 个具有真假的命题,给一些逻辑关系(例如 (p_1 vee p_2)),如果逻辑关系式子包含 (k) 个元,要求出 (n) 个命题的真假值满足所有逻辑关系。当 (k>3) 时,已经被证明为 (NPC) 问题,但是 (k=2) 时可以使用线性复杂度的算法解决,所以我们在这重点讨论 2-SAT 问题。

    做法

    注意到这样一个事实:(avee b Leftrightarrow eg a ightarrow b) ,同理,(avee b Leftrightarrow eg b ightarrow a) 。(详细可以参考数理逻辑)

    ( ightarrow) 被称为蕴含

    如果将 (n) 个命题看成 (2n) 个点,然后利用上面的事实建边的话,就可以转化为图论问题求解了。

    具体来说,例如,有命题 (p_1,p_2) ,逻辑关系为 (p_1vee p_2) ,我们将 (p_1) 拆成两个状态(图论中相应的两个点) (x_1, eg x_1) ,分别表示 (p_1)、取(p_2) 用相同方式处理,那么逻辑关系可以转化为图论中的两条有向边:(( eg x_1, x_2),~( eg x_2, x_1))

    用这样的方式,就可以将所给的命题以及逻辑关系转化为一个有向图了。

    对这个有向图,我们采用 (SCC缩点) ,如果一个命题的两个状态 (x_i,~ eg x_i) 同时出现在同一个强连通分量中,问题无解(因为不可能有 (x_i) 直接或间接地蕴含 ( eg x_i)

    否则,(x_i,~ eg x_i) 所在的强连通分量编号一定存在严格的偏序关系(通俗地说就是一定是一大一小),我们取编号小的那个状态作为命题 (p_i) 的取值即可。

    下面简单说明这样取即可保证正确:
    假如 ( eg a ightarrow b),由逆否命题性质,一定有 ( eg b ightarrow a) ,如果说 ( eg a)(b) 在同一强连通分量中,那么 ( eg b)(a) 一定在另一强连通分量中,不妨设 (a) 所在的强连通分量编号较小,我们选取了 (a) 。由强连通分量性质,(a)( eg b) 可以相互到达,所以 ( eg b) 一定要被选取,而事实上由构造方式知 ( eg b) 一定是被选取的,因此这样做能够保证正确性。

    模板题:(一样的)
    https://www.acwing.com/problem/content/2404/
    https://www.luogu.com.cn/problem/P4782

    代码

    #include<bits/stdc++.h>
    using namespace std;
    
    inline void read(int &x) {
        int s=0;x=1;
        char ch=getchar();
        while(ch<'0'||ch>'9') {if(ch=='-')x=-1;ch=getchar();}
        while(ch>='0'&&ch<='9') s=(s<<3)+(s<<1)+ch-'0',ch=getchar();
        x*=s;
    }
    
    const int N=2e6+5, M=2e6+5;
    int n, m;
    
    struct node{
    	int to, next;	
    }e[M];
    
    int h[N], tot;
    
    void add(int u, int v){
    	e[tot].to=v, e[tot].next=h[u], h[u]=tot++;
    }
    
    int ts, dfn[N], low[N];
    int stk[N], top;
    int id[N], cnt;
    bool ins[N];
    
    void tarjan(int u){
    	dfn[u]=low[u]=++ts;
    	stk[++top]=u, ins[u]=true;
    	for(int i=h[u]; ~i; i=e[i].next){
    		int go=e[i].to;
    		if(!dfn[go]){
    			tarjan(go);
    			low[u]=min(low[u], low[go]);
    		}else if(ins[go]) low[u]=min(low[u], dfn[go]);
    	}
    	
    	if(dfn[u]==low[u]){
    		int y;
    		cnt++;
    		do{
    			y=stk[top--], ins[y]=false, id[y]=cnt; 
    		}while(y!=u);
    	}
    }
    
    int res[N];
    
    int main(){
    	memset(h, -1, sizeof h);
    	read(n), read(m);
    	
    	while(m--){
    		int i, a, j, b; read(i), read(a), read(j), read(b);
    		i--, j--;
    		add(2*i+!a, 2*j+b), add(2*j+!b, 2*i+a);
    	}
    	
    	for(int i=0; i<2*n; i++) if(!dfn[i]) tarjan(i);
    	
    	for(int i=0; i<n; i++) if(id[2*i]==id[2*i+1]){
    		puts("IMPOSSIBLE");
    		return 0;
    	}else res[i]= id[2*i]>id[2*i+1];
    	
    	puts("POSSIBLE");
    	for(int i=0; i<n; i++) printf("%d ", res[i]);
    	cout<<endl;
    	
    	return 0;
    }
    
  • 相关阅读:
    HDU 1025 Constructing Roads In JGShining's Kingdom (DP+二分)
    HDU 1158 Employment Planning
    HDU 2059 龟兔赛跑
    Csharp 简单操作Word模板文件
    Csharp windowform datagridview Clipboard TO EXCEL OR FROM EXCEL DATA 保存datagridview所有數據
    Csharp 讀寫文件內容搜索自動彈出 AutoCompleteMode
    Csharp windowform controls clear
    CSS DIV大图片右上角叠加小图片
    Csharp DataGridView自定义添加DateTimePicker控件日期列
    Csharp 打印Word文件默認打印機或選擇打印機設置代碼
  • 原文地址:https://www.cnblogs.com/Tenshi/p/14781875.html
Copyright © 2011-2022 走看看