2-SAT 问题

推荐blog:

https://www.cnblogs.com/cjoieryl/p/8460181.html

https://www.cnblogs.com/-ZZB-/p/6635483.html

https://blog.csdn.net/jarjingx/article/details/8521690

2-SAT的定义:

  当前有一些集合,每个集合中有且仅有2个元素,且不能同时被选取,集合与集合间的元素存在矛盾关系,求可行解及其方案。

求解2-SAT的过程:

  ①根据题意判断模型并建边。

  ②:Tarjan求强连通分量。

  ③:判断可行性。

模型:

  ①:(A,B)不能同时取 A->B' A'->B

    选了A就只能选B',选了A'就只能选B

  ②:(A,B)不能同时不取  A'->B  B'->A

    选了A'必定选B,选了B'必定选A

  ③:(A,B)要么都取,要么都不取 A->B  B->A  A'->B' B'->A'

    选了A只能选B,选了B只能选A,选了A'只能选B',选了B'只能选A'

做Tarjan的原因:

  在连边的时候点与点之间是单向边,那么我们连着连着就可能出现强连通分量,而出现强连通分量就说明了这个强连通分量中只要选了一个点其他的点就必须全部被选,这样就可以判断有冲突的两个元素是否同时必须被选了。Tarjan是用来求强连通分量的。

输出方案:

  由于Tarjan是基于DFS实现的,2-SAT具有对称性,那么假如这一种方案可行,那么另一种方案也可行,并且这两种方案中的点的所属的强连通分量的编号也是有大小关系的。对于任意两个有冲突的元素中,所属编号大的是一种方案,编号小的又是另一种方案。

习题:

  Luogu 4782 【模板】2-SAT 问题:https://www.luogu.org/problemnew/show/P4782

   解题思路:

        模板题。。。

  

#include<bits/stdc++.h>
using namespace std;
#define re register int
#define ll long long
#define INF 0x3f3f3f3f
#define maxn 2000009
#define maxm
inline ll read()
{
    ll x=0,f=1;char ch=getchar();
    while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();}
    while(ch>='0'&&ch<='9'){x=(x<<1)+(x<<3)+(ll)(ch-'0');ch=getchar();}
    return x*f;
}
stack<int>s;
int head[maxn],dfn[maxn],low[maxn],belong[maxn];
int n,m,k,ans,tot,cnt,id,block;
struct edge
{
    int to,nxt;
}p[maxn<<2];
void add(int x,int y)
{
    p[++cnt]={y,head[x]},head[x]=cnt;
} 
 
void Tarjan(int u)
{
    dfn[u]=low[u]=++id,s.push(u);
    for(int i=head[u];i;i=p[i].nxt)
    {
        int v=p[i].to;
        if(!dfn[v])
        {
            Tarjan(v);
            low[u]=min(low[u],low[v]);
        }
        else if(!belong[v])
            low[u]=min(low[u],dfn[v]);
    }
    if(dfn[u]==low[u])
    {
        ++block;
        while(1)
        {
            int v=s.top();s.pop();
            belong[v]=block;
            if(u==v)
                break;
        }
    }
}
bool TWO_SAT()
{
    for(int i=1;i<=2*n;i++)
        if(!dfn[i])
            Tarjan(i);
    for(int i=1;i<=n;i++)
        if(belong[i]==belong[i+n])
            return false;
    return true;
}
int main()
{
//    freopen(".in","r",stdin);
//    freopen(".out","w",stdout);
    n=read(),m=read();
    for(int i=1;i<=m;i++)
    {
        int x=read(),fx=read(),y=read(),fy=read();
        int gx=!fx,gy=!fy;
        add(x+gx*n,y+fy*n);
        add(y+gy*n,x+fx*n);
    }
    if(TWO_SAT())
    {
        puts("POSSIBLE");
        for(int i=1;i<=n;i++)
            if(belong[i]<belong[i+n])
                printf("0 ");
            else
                printf("1 ");
    }
    else
        puts("IMPOSSIBLE");
    fclose(stdin);
    fclose(stdout);
    return 0;
}
View Code

  Loj #10097. 「一本通 3.5 练习 5」和平委员会:https://loj.ac/problem/10097

  解题思路:还是模板题。。

  

#include<bits/stdc++.h>
using namespace std;
#define re register int
#define ll long long
#define INF 0x3f3f3f3f
#define maxn 16009
#define maxm 20009
inline ll read()
{
    ll x=0,f=1;char ch=getchar();
    while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();}
    while(ch>='0'&&ch<='9'){x=(x<<1)+(x<<3)+(ll)(ch-'0');ch=getchar();}
    return x*f;
}
stack<int>s;
int head[maxn],dfn[maxn],low[maxn],belong[maxn];
struct edge
{
    int to,nxt;
}p[maxm<<1];
int n,m,k,ans,tot,cnt,id,block; 

void add(int x,int y)
{
    p[++cnt]={y,head[x]},head[x]=cnt;
}


void Tarjan(int u)
{
    dfn[u]=low[u]=++id,s.push(u);
    for(int i=head[u];i;i=p[i].nxt)
    {
        int v=p[i].to;
        if(!dfn[v])
        {
            Tarjan(v);
            low[u]=min(low[u],low[v]);
        }
        else if(!belong[v])
            low[u]=min(low[u],dfn[v]);
    }
    if(dfn[u]==low[u])
    {
        ++block;
        while(1)
        {
            int v=s.top();s.pop();
            belong[v]=block;
            if(u==v)
                break;
        }
    }
}
bool TWO_SAT()
{
    for(int i=1;i<=n*2;i++)
        if(!dfn[i])
            Tarjan(i);
    for(int i=1;i<=n*2;i+=2)
        if(belong[i]==belong[i+1])
            return false;
    return true;
}
priority_queue<int>q;
int main()
{
//    freopen(".in","r",stdin);
//    freopen(".out","w",stdout);
    n=read(),m=read();
    for(int i=1;i<=m;i++)
    {
        int x=read(),y=read();
        int fx=(x&1)?1:0,fy=(y&1)?1:0;
        add(x,fy==1?y+1:y-1);
        add(y,fx==1?x+1:x-1);
    }
    if(TWO_SAT())
    {
        for(int i=1;i<=2*n;i+=2)
             if(belong[i]<belong[i+1])
                 q.push(-i);
            else
                q.push(-(i+1));
        while(q.size())
        {
            printf("%d
",-q.top());
            q.pop(); 
        }
    } 
    else
        puts("NIE");
    fclose(stdin);
    fclose(stdout);
    return 0;
}
View Code
原文地址:https://www.cnblogs.com/Dxy0310/p/9809845.html