zoukankan      html  css  js  c++  java
  • 2-SAT问题(题目)

    2-SAT问题

    现有一个由N个布尔值组成的序列A,给出一些限制关系,比如A[x] AND A[y]=0、A[x] OR A[y] OR A[z]=1等,要确定A[0..N-1]的值,使得其满足所有限制关系。这个称为SAT问题,特别的,若每种限制关系中最多只对两个元素进行限制,则称为2-SAT问题。

    因为如果元素多个,算不出来,这是NP问题

    https://www.cnblogs.com/kuangbin/archive/2012/10/05/2712429.html

    洛谷的讲解非常好:https://www.luogu.com.cn/problemnew/solution/P4782

    解决方法:强连通分量+拓扑排序

    建图:通过限制关系建图

    ps. 注意点的个数为2*N,因为x为i,-x为i+n

    建好图之后,求出强连通分量SCC,一个SCC内部都是互相依赖的,所以一个SCC以内不能出现夫妻关系(x与-x),如果所有的SCC内部都没有夫妻关系,就有合法的组合

    把每个SCC看成是一个点,构成了DAG图,进行反图的拓扑排序,再选中点时排除图中相矛盾的点,就能找到合法的组合。

    洛谷的讲解:

     

     最后的输出为col[i]>col[i+n]

    P4782 【模板】2-SAT 问题

    #include<iostream>
    #include<cstring>
    #include<cmath>
    #include<algorithm>
    #include<stack>
    #include<cstdio>
    #include<queue>
    #include<map>
    #include<vector>
    #include<set>
    using namespace std;
    const int maxn=4e6+10;
    const int INF=0x3fffffff;
    typedef long long LL;
    //2-SAT模板问题
    //洛谷的讲解非常好:https://www.luogu.com.cn/problemnew/solution/P4782
    int col[maxn],head[maxn];
    int low[maxn],num[maxn],dfn,ans;
    int vis[maxn],sta[maxn],top;
    int n,m;
    struct node{
    	int to,next;
    }ed[maxn];
    int cnt;
    void add(int f,int t){
    	ed[++cnt].to=t;
    	ed[cnt].next=head[f];
    	head[f]=cnt;
    }
    
    /*
    void create(){
    	scanf("%d %d",&n,&m);
    	int x,y,a,b;
    	for(int i=0;i<m;i++){
    		scanf("%d %d %d %d",&x,&a,&y,&b);
    		if(a&&b){  // a, b 都真,-a -> b, -b -> a
    			g[a+n].push_back(b);
    			g[b+n].push_back(a);
    		}
    		else if(!a&&b){// a 假 b 真,a -> b, -b -> -a
    			g[a].push_back(b);
    			g[b+n].push_back(a+n);
    		}
    		else if(a&&!b){  // a 真 b 假,-a -> -b, b -> a
    			g[a+n].push_back(n+b);
    			g[b].push_back(a);
    		}
    		else if(!a&&!b){ // a, b 都假,a -> -b, b -> -a
    			g[a].push_back(b+n);
    			g[b].push_back(a+n);
    		}
    	}
    }  
    */
    /*   更简单的方式 
    n = read(), m = read();
    for (int i = 0; i < m; ++i) {
        int a = read(), va = read(), b = read(), vb = read();
        g[a + n * (va & 1)].push_back(b + n * (vb ^ 1));
        g[b + n * (vb & 1)].push_back(a + n * (va ^ 1));
    }
    */
    
    void tarjin(int u,int fa){
    	low[u]=num[u]=++dfn;
    	sta[++top]=u;
    	vis[u]=1;
    	for(int i=head[u];i;i=ed[i].next){
    		int v=ed[i].to;
    		if(v==fa) continue;
    		if(!num[v]){
    			tarjin(v,u);
    			low[u]=min(low[u],low[v]);
    		}
    		else if(vis[v]){
    			low[u]=min(low[u],num[v]);
    		}
    	}
    	if(low[u]==num[u]){
    		ans++;
    		while(sta[top]!=u){
    			col[sta[top]]=ans;
    			vis[sta[top]]=0;
    			top--;
    		}
    		col[sta[top]]=ans;
    		vis[sta[top]]=0;
    		top--; //给u 
    	}
    }
    int main(){
    	scanf("%d %d",&n,&m);
    	int a,b,x,y;
    	for(int i=0;i<m;i++){
    		scanf("%d %d %d %d",&a,&x,&b,&y);
    		if(x==0&&y==0)      //"如果第a个数为0或第b个数为0"至少满足其一 
            {
                add(a+n,b);     //a=1则b=0 
                add(b+n,a);     //b=1则a=0 
            }
            if(x==0&&y==1)      //"如果第a个数为0或第b个数为1"至少满足其一 
            {
                add(a+n,b+n);   //a=1则b=1 
                add(b,a);       //b=0则a=0 
            }
            if(x==1&&y==0)      //"如果第a个数为1或第b个数为0"至少满足其一
            {
                add(a,b);       //a=0则b=0 
                add(b+n,a+n);   //b=1则a=1 
            }
            if(x==1&&y==1)      //"如果第a个数为1或第b个数为1"至少满足其一
            {
                add(a,b+n);     //a=0则b=1 
                add(b,a+n);     //b=0则a=1 
            }
    	}
    	for(int i=1;i<=(n<<1);i++){
    		if(!num[i]) tarjin(i,0);
    	}
    	//就可以得到答案了,如果两个取值是在同一个SCC中就不可能
    	for(int i=1;i<=n;i++){
    		if(col[i]==col[i+n]){
    			printf("IMPOSSIBLE
    ");
    			return 0;
    		}
    	} 
    	printf("POSSIBLE
    ");
    	for(int i=1;i<=n;i++){
    		printf("%d ",col[i]>col[i+n]);  //注意这个输出 
    	}
    return 0;
    }
    

    P4171 [JSOI2010]满汉全席

    #include<iostream>
    #include<cstring>
    #include<cmath>
    #include<algorithm>
    #include<stack>
    #include<cstdio>
    #include<queue>
    #include<map>
    #include<vector>
    #include<set>
    using namespace std;
    const int maxn=4010;
    const int INF=0x3fffffff;
    typedef long long LL;
    int readd(){
    	int f=1,x=0;
    	char ss=getchar();
    	while(ss<'0'||ss>'9') {
    		if(ss=='-') f=-1;
    		ss=getchar();
    	}
    	while(ss>='0'&&ss<='9'){
    		x=x*10+ss-'0';
    		ss=getchar();
    	}
    	return f*x;
    }
    int n,m,k;
    int head[maxn],low[maxn],num[maxn],col[maxn],st[maxn],vis[maxn];
    int top,ans,cnt,dfn;
    struct node{
    	int to,next;
    }ed[maxn];
    
    void adde(int f,int t){
    	ed[++cnt].to=t;
    	ed[cnt].next=head[f];
    	head[f]=cnt;
    }
    void tarjin(int u,int fa){
    	low[u]=num[u]=++dfn;
    	vis[u]=1;
    	st[++top]=u;
    	for(int i=head[u];i;i=ed[i].next){
    		int v=ed[i].to;
    		if(!num[v]){
    			tarjin(v,u);
    			low[u]=min(low[u],low[v]);
    		}
    		else if(vis[v]){
    			low[u]=min(low[u],num[v]);
    		}
    	}
    	if(low[u]==num[u]){
    		ans++;
    		do{
    			vis[st[top]]=0;
    			col[st[top]]=ans;
    			top--;
    		}while(u!=st[top]);
    	}
    }
    void inti(){
    	memset(head,0,sizeof(head));
    	memset(vis,0,sizeof(vis));
    	memset(low,0,sizeof(low));
    	memset(st,0,sizeof(st));
    	memset(num,0,sizeof(num));
    	memset(col,0,sizeof(col));
    	cnt=0,dfn=0,top=0,ans=0;
    }
    int main(){
    	scanf("%d",&k);
    	while(k--){
    		inti();
    		scanf("%d %d",&n,&m);
    		char str1[10],str2[10];
    		for(int i=0;i<m;i++){
    			scanf("%s %s",str1,str2);
    			int a=0,b=0;
    			for(int j=1;j<strlen(str1);j++) a=a*10+(str1[j]-'0');
    			for(int j=1;j<strlen(str2);j++) b=b*10+(str2[j]-'0');
    			if(str1[0]=='m'&&str2[0]=='m'){  //0 0
    				adde(a+n,b);  //1 0
    				adde(b+n,a);   //1 0
    			}
    			else if(str1[0]=='m'&&str2[0]=='h'){  //0 1
    				adde(a+n,b);  //1 0
    				adde(b,a);   //0 0
    			}
    			else if(str1[0]=='h'&&str2[0]=='m'){ //1 0
    				adde(a,b);  //0 0
    				adde(b+n,a+n);    //1 1
    			}
    			else if(str1[0]=='h'&&str2[0]=='h'){  // 1 1
    				adde(a,b+n);
    				adde(b,a+n) ;  //0 1
    			}
    		}
    		for(int i=1;i<=(n<<1);i++){
    			if(!num[i]) tarjin(i,0);
    		}
    		bool flag=true;
    		for(int i=1;i<=n;i++){
    			if(col[i]==col[i+n]){
    				flag=0;
    				break;
    			}
    		}
    		if(flag) printf("GOOD
    ");
    		else printf("BAD
    ");
    	}
    return 0;
    }
    

    P5782 [POI2001]和平委员会

    #include<iostream>
    #include<cstring>
    #include<cmath>
    #include<algorithm>
    #include<stack>
    #include<cstdio>
    #include<queue>
    #include<map>
    #include<vector>
    #include<set>
    using namespace std;
    const int maxn=40010;
    const int INF=0x3fffffff;
    typedef long long LL;
    int head[maxn],vis[maxn],low[maxn],num[maxn],col[maxn];
    int ans,cnt,top,dfn,n,m;
    stack<int> st;
    struct node{
    	int t,next;
    }ed[maxn];
    void add(int u,int v){
    	ed[++cnt].next=head[u];
    	ed[cnt].t=v;
    	head[u]=cnt;
    }
    void tarjin(int u){
    	low[u]=num[u]=++dfn;
        st.push(u); 
    	vis[u]=1;
        for(int i=head[u];i;i=ed[i].next)
        {
            int v=ed[i].t;
            if(!num[v]){ 
    		tarjin(v); 
    		low[u]=min(low[u],low[v]);}
            else if(vis[v])
            low[u]=min(low[u],num[v]);
        }
        if(low[u]==num[u])
        {
            int v; 
    		ans++;
            do{
                v=st.top();
                st.pop(); 
    			vis[v]=0;
                col[v]=ans;
            }
            while(v!=u);
        }
    }
    int chan(int x){
    	return ((x%2)? x+1:x-1);
    }
    int main(){
    	scanf("%d %d",&n,&m);
    	int x,y;
    	for(int i=0;i<m;i++){
    		scanf("%d %d",&x,&y);
    		add(x,chan(y));
    		add(y,chan(x));
    	}
    	for(int i=1;i<=(n<<1);i++){
    		if(!num[i]) tarjin(i);
    	}
    	bool flag=1;
    	for(int i=1;i<=2*n;i++){   //每个党派有两个代表,所以要乘 2
    		if(i%2==1&&col[i]==col[i+1]) {
    			flag=0;
    			break;
    		}
    	}
    	if(!flag) printf("NIE
    ");
    	else{
    		for(int i=1;i<=2*n;i++){
    			if(col[i]<col[chan(i)]){
    				printf("%d
    ",i);
    			}
    		}
    	}
    return 0;
    }
    

      

  • 相关阅读:
    三部曲搭建本地nuget服务器(图文版)
    用批处理编译*.sln工程
    一组无序的整数找出出现次数大于一半的数字
    程序打怪升级之旅
    web开发有那些牛逼东西可以用
    Visual Studio for mac从入门到放弃1
    svn自动更新服务器最新代码
    WinRT支持GB2312
    初试Node —— node.js的安装
    为什么要重写equals方法和hashcode方法
  • 原文地址:https://www.cnblogs.com/shirlybaby/p/12550518.html
Copyright © 2011-2022 走看看