2-SAT【模板】

摘自http://www.cnblogs.com/kuangbin/archive/2012/10/05/2712429.html

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

由于在2-SAT问题中,最多只对两个元素进行限制,所以可能的限制关系共有11种:

A[x]

NOT A[x]

A[x] AND A[y]

A[x] AND NOT A[y]

A[x] OR A[y]

A[x] OR NOT A[y]

NOT (A[x] AND A[y])

NOT (A[x] OR A[y])

A[x] XOR A[y]

NOT (A[x] XOR A[y])

A[x] XOR NOT A[y]

进一步,A[x] AND A[y]相当于(A[x]) AND (A[y])(也就是可以拆分成A[x]与A[y]两个限制关系),NOT(A[x] OR A[y])相当于NOT A[x] AND NOT A[y](也就是可以拆分成NOT A[x]与NOT A[y]两个限制关系)。因此,可能的限制关系最多只有9种。

2-SAT问题在大多数时候表现成以下形式:有N对物品,每对物品中必须选取一个,也只能选取一个,并且它们之间存在某些限制关系(如某两个物品不能都选,某两个物品不能都不选,某两个物品必须且只能选一个,某个物品必选)等,这时,可以将每对物品当成一个布尔值(选取第一个物品相当于0,选取第二个相当于1),如果所有的限制关系最多只对两个物品进行限制,则它们都可以转化成9种基本限制关系,从而转化为2-SAT模型。

【建模】

可以构造有向图G,G中包含2*N个顶点,前N个顶点(1~N)表示第i个元素能被选择,后N个顶点(N+1~2*N)表示第i个元素不能被选择。Ai和A(i+N)不能同时被选择。同理Ai + Bj = ~( A(i+N) + B(j+N) ),A(i+N)和B(j+N)不能同时被选。选中~A,必须选择B;如果选择~B,必须选择A。

若图中i到j有路径,则若i选,则j也要选;或者说,若j不选,则i也不能选。

#include<iostream>
#include<algorithm>
#include<cstdio>
#include<cstring>
#include<cmath>
#include<queue>
using namespace std;
const int MAXN = 2200;
const int MAXM = MAXN*MAXN;

struct EdgeNode
{
    int to;
    int next;
}Edges[MAXM];

int Head[MAXN];
int dfn[MAXN],low[MAXN],belong[MAXN],Stack[MAXN],vis[MAXN];
int m,id,lay,scc,N,M;
//belong[]来判断i和i+N是否在一个强连通分量里
void AddEdges(int u,int v)
{
    Edges[id].to = v;
    Edges[id].next = Head[u];
    Head[u] = id++;
}

void TarBFS(int pos)
{
    dfn[pos] = low[pos] = ++lay;
    Stack[m++] = pos;
    vis[pos] = 1;

    for(int i = Head[pos]; i != -1; i = Edges[i].next)
    {
        int v = Edges[i].to;
        if( !dfn[v] )
        {
            TarBFS(v);
            low[pos] = min(low[pos],low[v]);
        }
        else if(vis[v])
            low[pos] = min(low[pos],low[v]);
    }
    int v;
    if(dfn[pos] == low[pos])
    {
        ++scc;
        do
        {
            v = Stack[--m];
            belong[v] = scc;
            vis[v] = 0;
        }while(v != pos);
    }
}

int main()
{
    int u,v;
    while(~scanf("%d%d",&N,&M))
    {
        id = m = scc = lay = 0;
        memset(Head,-1,sizeof(Head));
        memset(vis,0,sizeof(vis));
        memset(low,0,sizeof(low));
        memset(dfn,0,sizeof(dfn));
        memset(belong,0,sizeof(belong));
        for(int i = 0; i < M; ++i)
        {
            scanf("%d%d",&u,&v);
            int a = abs(u);
            int b = abs(v);

            if(u > 0 && v > 0)
            {   //a、b至少一个被选中
                AddEdges(a+N,b);      //如果a不被选,b就必须被选
                AddEdges(b+N,a);      //如果b不被选,a就必须被选
            }

            if(u < 0 && v < 0)
            {   //a、b至少有一个不被选中
                AddEdges(a,b+N);      //如果a被选,b就必须不被选
                AddEdges(b,a+N);      //如果b被选,a就必须不被选
            }

            if(u > 0 && v < 0)
            {   //a被选中和b不被选中两件事至少发生一件
                AddEdges(a+N,b+N);    //如果a不被选中,b必须不被选中
                AddEdges(b,a);        //如果b被选中,那么a必须被选中
            }

            if(u < 0 && v > 0)
            {   //a不被选中和b被选中至少发生一件
                AddEdges(a,b);        //如果a被选中,b必须被选中
                AddEdges(b+N,a+N);    //如果b不被选中,a必须不被选中
            }
        }

        for(int i = 1; i <= 2*N; ++i)
            if( !dfn[i] )
                TarBFS(i);
        int ans = 1;
        for(int i = 1; i <= N; ++i)
        {
            if(belong[i] == belong[i+N])    //如果i和i+N同在一个连通分量里,则2-SAT不满足
            {
                ans = 0;
                break;
            }
        }

        printf("%d\n",ans); //不存在就满足2-SAT
    }

    return 0;
}
时间: 2024-10-17 19:48:30

2-SAT【模板】的相关文章

TwoSAT算法模板

该模板来自大白书 [解释] 给多个语句,每个语句为“ Xi为真(假) 或者 Xj为真(假)” 每个变量和拆成两个点 2*i为假, 2*i+1为真 “Xi为真 或 Xj为真”  等价于 “Xi为假 –>  Xj为真”. DFS算法没有回溯过程. [函数说明] 模板bfs函数在模板外一般用不到 void init(int n) :初始化 void add(int x,int xval,int y,int yval) :添加边,x,y为节点编号,xval=1表示真,xval=0表示假,yval同理 b

网页模板pug基本语法

该博客首发于www.litreily.top Pug – robust, elegant, feature rich template engine for Node.js pug原名jade,因版权问题更名为pug,即哈巴狗.与hexo默认模块ejs一样,pug也是一个模板引擎,可用于快速的网站开发,当然也可以用于静态博客网站的设计.本站点现时所用主题manupassant也使用了pug. 本文针对Hexo中使用pug的情况为例,说明其基本语法. 安装 # common install npm

Vue.js项目模板搭建

前言 从今年(2017年)年初起,我们团队开始引入「Vue.js」开发移动端的产品.作为团队的领头人,我的首要任务就是设计 整体的架构 .一个良好的架构必定是具备丰富的开发经验后才能搭建出来的.虽然我有多年的前端开发经验,但就「Vue.js」来说,仍然是个新手.所幸「Vue.js」有一个配套工具「Vue-CLI」,它提供了一些比较成熟的项目模板,很大程度上降低了上手的难度.然而,很多具体的问题还是要自己思考和解决的. 项目划分 我们公司的H5产品大部分是嵌套在手机客户端里面的页面.每个项目的功能

ac自动机基础模板(hdu2222)

In the modern time, Search engine came into the life of everybody like Google, Baidu, etc. Wiskey also wants to bring this feature to his image retrieval system. Every image have a long description, when users type some keywords to find the image, th

hdu 2966 In case of failure kdtree模板题

问求每个点距离平方的最小的点 kd-tree模板题…… 1 #include<bits/stdc++.h> 2 #define cl(a,b) memset(a,b,sizeof(a)) 3 #define debug(x) cerr<<#x<<"=="<<(x)<<endl 4 using namespace std; 5 typedef long long ll; 6 typedef pair<int,int>

eclipse添加xml模板

//因为学javaee,中框架,,感觉配置文件好多, window-preferences-xml-xmlfiles-editor-templates-选中模板,-edit

POJ3528 HDU3662 三维凸包模板

POJ3528 HDU3662 第一道题 给定若干点 求凸包的表面积,第二题 给定若干点就凸包的面数. 简单说一下三维凸包的求法,首先对于4个点假设不共面,确定了唯一四面体,对于一个新的点,若它不在四面体内,为了让它进入凸包, 则对于所有凸包上的边,若边的一面是该点可以看到的而另一面看不到,则该点与该边构成的面要加入凸包. 模板代码非常清晰, #include<stdio.h> #include<algorithm> #include<string.h> #includ

zabbix用自带的模板监控mysql

先看一下zabbix自带的mysql模板监控项: #很少是吧,没事生产环境一般我们不用,下一篇将介绍生产环境用的另一种mysql监控. 配置zabbix自带的模板监控mysql数据库:

小程序砸金蛋、外卖模板上线啦,快到酷客多商户后台更新!

最近,微信小程序官方发文不断,又开放十几项接口,逐步给企业主带来跟多福利.于此同时,酷客多研发团队也保持着一贯的研发和版本迭代速度,此次版本主要新增幸运砸金蛋.外卖模板.意见反馈三个模块 1.新增幸运砸金蛋,大奖中不停 通过此功能可增加平台趣味性,增强用户粘性,刺激用户二次消费,是与用户互动的一大利器. 2新增外卖模板,外卖送起来 此模板是餐饮企业的福利,可在注册或者酷客多商户管理后台直接选择此模板,瞬间让您的小程序首页变的高大上,从此再也不用担心第三方外卖平台高额的佣金和账期了,因为酷客多只提

C++学习笔记50:队列类模板

队列是只能向一端添加元素,从另一端删除元素的线性群体 循环队列 在想象中将数组弯曲成环形,元素出队时,后继元素不移动,每当队尾达到数组最后一个元素时,便再回到数组开头. 队列类模板 //Queue.h #ifndef QUEUE_H #define QUEUE_H #include <cassert> //类模板的定义 template <class T, int SIZE = 50> class Queue { private: int front, rear, count; T