zoukankan      html  css  js  c++  java
  • [洛谷P4782][题解]2-SAT问题

    题目

    0.定义

    先定义(k-SAT)问题:
    给出(n)个数和(m)个形如(x_1oplus x_2opluscdotsoplus x_k=0/1)的关系式((oplus)(land ,lor)等运算符),询问是否有解,如果有解,求出(x_1,x_2,cdots ,x_k)的值。
    (k>2)的已经证明是(NPC)了,我们只考虑(2-SAT)问题

    1.解法

    我们已经知道问题是什么样了,接下来该考虑怎么求了
    题中给的关系都不确定,我们考虑如何转成确定的关系
    遇到这类问题我们常常考虑拆点,那么不妨也在这题试试:
    把每个点(i)拆成(i,lnot i),然后考虑连边
    这里我们举一个很好理解的例子:(iland j=1),求(i,j)
    第一种情况:(i=0)
    此时(j)只能等于1,于是从(lnot i)连到(j)
    第二种情况:(j=0)
    此时(i)只能等于1,于是从(lnot j)连到(i)
    剩下的情况可以自己推一下,代码中用了一个比较方便的位运算简化版
    然后呢?
    首先判断是否有解
    可以很显然地想到:存在点(i)(lnot i)在一个强连通分量里(Leftrightarrow)无解
    这句话的意思就是如果有一个点为1,但是经过一些推导之后得到该点为0,那么就无解
    那么如果有解的话我们怎么求出这个解呢?
    直接亮出正解:选择(i,lnot i)中拓扑序较大的点
    因为(Tarjan)是回溯的时候记录,所以要输出编号小的

    2.代码

    省略了缺省源

    #define N 1000000 
    #define M 2000010
    int n,m,dfn[M],low[M],bel[M],ins[M],tim,num;
    struct Edge {
    	int nxt,to;
    }e[M];
    int head[M],cnt;
    inline void ade(int u,int v){
    	e[++cnt].to=v;
    	e[cnt].nxt=head[u];
    	head[u]=cnt;
    }
    stack<int>s;//以下为Tarjan板子 
    void Tarjan(int now){
    	dfn[now]=low[now]=++tim;
    	s.push(now),ins[now]=1;
    	for(rg int i=head[now];i;i=e[i].nxt){
    		int v=e[i].to;
    		if(!dfn[v]){
    			Tarjan(v);
    			low[now]=min(low[now],low[v]);
    		}else if(ins[v]){
    			low[now]=min(low[now],dfn[v]);
    		}
    	}
    	if(dfn[now]==low[now]){
    		num++;
    		do {
    			bel[now]=num;
    			now=s.top();
    			s.pop(),ins[now]=0;
    		}while(dfn[now]!=low[now]);
    	}
    }
    int main(){
    	Read(n),Read(m);
    	for(rg int i=1;i<=m;i++){
    		int a,b,ba,bb;
    		Read(a),Read(ba),Read(b),Read(bb);
    		ade(a+n*(ba&1),b+n*(bb^1));//小小的位运算简化代码 
    		ade(b+n*(bb&1),a+n*(ba^1));
    	}
    	for(rg int i=1;i<=(n<<1);i++){
    		if(!dfn[i])Tarjan(i);
    	}
    	for(rg int i=1;i<=n;i++){
    		if(bel[i]==bel[i+n]){//判断无解 
    			cout<<"IMPOSSIBLE"<<endl;
    			return 0;
    		}
    	}
    	cout<<"POSSIBLE"<<endl;
    	for(rg int i=1;i<=n;i++){//注意要用小于号 
    		cout<<(bel[i]<bel[i+n])<<" ";
    	}
    	return 0;
    }
    

    3.完结撒花

  • 相关阅读:
    ios 初吻
    Oracle 的sql*plus编辑器真够简陋
    人月神话:软件界面交互和易用性改进总结
    IIS管理
    【C#】自定义新建一个DataTable(3列),循环3维矩形数组往其填充数据
    window.open完美替代window.showModalDialog
    Sql Server之ORDER BY不规则排序.如:中文月份排序
    JS设计模式之单体模式(Singleton)
    关于第三方库引用的问题
    [JSTL] 使用本地化资源文件的方法
  • 原文地址:https://www.cnblogs.com/juruoajh/p/12725766.html
Copyright © 2011-2022 走看看