码迷,mamicode.com
首页 > 其他好文 > 详细

2-SAT【模板】

时间:2015-05-05 19:36:48      阅读:116      评论:0      收藏:0      [点我收藏+]

标签:

摘自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;
}

2-SAT【模板】

标签:

原文地址:http://blog.csdn.net/lianai911/article/details/45506175

(0)
(0)
   
举报
评论 一句话评论(0
登录后才能评论!
© 2014 mamicode.com 版权所有  联系我们:gaon5@hotmail.com
迷上了代码!