2-SAT【模板】

来源:互联网 发布:tc控制网络延迟的时间 编辑:程序博客网 时间:2024/05/21 22:45

摘自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;}
0 0
原创粉丝点击