2-SAT 学习笔记

Introduction \raisebox{-15pt}{\Large\texttt{Introduction}} Introduction


2-SAT 是用来解决多个类似 A为 true/false 或者 B 为 true/false 的约束之类的问题。
比如说有两个约束:A=true 或 B=falseB=true 或 C=true ,此时就可以这样:A=true,B=true,C=true
Before   you   read \raisebox{-15pt}{\Large\texttt{Before you read}} Before you read


在看懂这篇文章之前,你需要学会 Tarjan \texttt{Tarjan} Tarjan
Solution \raisebox{-15pt}{\Large\texttt{Solution}} Solution


首先,对于一个约束,例如 A=true 或 B=false ,要让他满足条件,那么 A 不满足条件时 B 一定满足条件,B 不满足条件时 A 一定满足条件 。例如 A=false 的时候 B=falseB=true 的时候 A=true

那么我们可以根据这两个条件来进行建边。为方便表示,定义 A f A_f Af 表示 A=false A t A_t At 表示 A=true 。根据上面的条件,可以建 A f → B f A_f\to B_f AfBf B t → A t B_t\to A_t BtAt 这两条边。

现在我们建出了一个图,我们知道,强连通分量中的点是可以互相到达的。也就是说,这里面的一个点满足条件,这个强连通分量里的点也一定满足条件 。于是我们知道,如果有一个变量 A A A A t A_t At A f A_f Af 在同一个强连通分量里面,则无解。所以在这个图上面跑一遍 Tarjan \texttt{Tarjan} Tarjan 即可。

现在判完了无解,我们考虑如何输出。

其实在 Tarjan \texttt{Tarjan} Tarjan 的过程中,已经标好了连通块的顺序,我们只需要看哪个排在前面,就输出哪个就行了。但是注意, Tarjan \texttt{Tarjan} Tarjan 跑出来的拓扑序时倒序,所以在判断时输出的是编号较大的那一个答案。
Code \raisebox{-15pt}{\Large\texttt{Code}} Code


【实现细节】
1. 1. 1. 注意建边有没有建错。
2. 2. 2. 输出答案比较时符号要对。
3. 3. 3. 确保你的 Tarjan \texttt{Tarjan} Tarjan 是对的。
4. 4. 4. 数组要开双倍。

#include <bits/stdc++.h>

using namespace std;

const int   N=2000010;

vector<int> road[N];
stack<int>  st;
int         dfs[N],conn[N];
int         belong[N];
int         state[N];
int         tot,blk;
int         n,m;

void tarjan(int root)
{
    dfs[root]=conn[root]=++tot;
    st.push(root);
    state[root]=1;
    for(int i=0; i<road[root].size(); i++)
    {
        int temp=road[root][i];
        if(!dfs[temp])
            tarjan(temp),conn[root]=min(conn[root],conn[temp]);
        if(state[temp])
            conn[root]=min(conn[root],dfs[temp]);
    }
    if(dfs[root]==conn[root])
    {
        blk++;
        while(st.top()!=root)
        {
            belong[st.top()]=blk;
            state[st.top()]=0;
            st.pop();
        }
        belong[root]=blk;
        state[root]=0;
        st.pop();
    }
    return;
}

void work()
{
    /* Code */
    cin>>n>>m;
    for(int i=1; i<=m; i++)
    {
        int x,y,z,alpha;
        cin>>x>>y>>z>>alpha;
        road[x+n*(1-y)].push_back(z+n*alpha);
        road[z+n*(1-alpha)].push_back(x+n*y);
    }
    for(int i=1; i<=2*n; i++)
        if(!dfs[i])
            tarjan(i);
    for(int i=1; i<=n; i++)
        if(belong[i]==belong[i+n])
        {
            cout<<"IMPOSSIBLE"<<'\n';
            return;
        }
    cout<<"POSSIBLE"<<'\n';
    for(int i=1; i<=n; i++)
        cout<<(belong[i]>belong[i+n])<<' ';
    cout<<'\n';
    return;
}

signed main()
{
    ios::sync_with_stdio(false);
    cin.tie(0);
    int t;
    t=1;
    while(t--)
        work();
    return 0;
}
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值