• Luogu P4782 【模板】2-SAT 问题


    传送门

    SAT(Satisfiability)问题:

    有n个$bool$变量,m个需要满足的条件,形如“xi为true || xj为false || xk为 false…”。

    给每个变量赋值,使得所有条件得到满足。

    SAT问题已被证明为NP完全,只能暴力枚举求解。

    特别地,如果每个条件中约定的变量只有2个,那么就可以用tarjan强连通分量求解,即2-SAT问题。

    感性理解:

    假设有一些“若x,则y”的条件,将$x$-> $y$连边。

    建图后,使用tarjan缩点,若发现x和非x在同一连通块内,则一定无解。

    那么,对于“x||y”的条件,可以把它转变为“若!x,则y”&&“若!y,则x”。

    将以上两个条件连边。一共有n个节点,!i的编号可以表示为i+n。

    tarjan缩点后,检查点1~n是否有$col[i] = col[!i]$,若有则无解。

    tarjan得出连通块的编号col[]恰好是拓扑序的逆序。拓扑序在后边的,对其他的节点影响小。

    所以输出方案时,$col[i] < col[!i]$则选i,否则选-i。

    代码如下

    #include<cstdio>
    #include<iostream>
    #include<cmath>
    #include<cstring>
    #define MogeKo qwq
    using namespace std;
    const int maxn = 2e6+10;
    int n,m,x,y,a,b,cnt,num,tot,top;
    int dfn[maxn],low[maxn],sta[maxn],col[maxn];
    bool insta[maxn];
    int head[maxn],to[maxn],nxt[maxn];
    
    void add(int x,int y) {
        to[++cnt] = y;
        nxt[cnt] = head[x];
        head[x] = cnt;
    }
    
    void tarjan(int u) {
        dfn[u] = low[u] = ++tot;
        insta[u] = true;
        sta[++top] = u;
        for(int i = head[u]; i; i = nxt[i]) {
            int v = to[i];
            if(!dfn[v]) {
                tarjan(v);
                low[u] = min(low[u],low[v]);
            } else if(insta[v])
                low[u] = min(low[u],dfn[v]);
        }
        if(dfn[u] == low[u]) {
            col[u] = ++num;
            while(sta[top] != u) {
                int v = sta[top--];
                col[v] = col[u];
                insta[v] = false;
            }
            top--;
            insta[u] = false;
        }
    }
    
    int main() {
        scanf("%d%d",&n,&m);
        for(int i = 1; i <= m; i++) {
            scanf("%d%d%d%d",&x,&a,&y,&b);
            add(x+(!a)*n, y+b*n);
            add(y+(!b)*n, x+a*n);
        }
        for(int i = 1; i <= 2*n; i++)
            if(!dfn[i]) tarjan(i);
        for(int i = 1; i <= n; i++)
            if(col[i] == col[i+n]) {
                printf("IMPOSSIBLE
    ");
                return 0;
            }
        printf("POSSIBLE
    ");
        for(int i = 1; i <= n; i++){
            if(col[i] < col[i+n]) printf("0 ");
            else printf("1 ");
        }
        return 0;
    }
    View Code

    棵题:Luogu P4171 [JSOI2010]满汉全席

  • 相关阅读:
    38.进程及同步异步的概念理解
    37.图灵接口及电脑语音聊天
    36.HTTP协议
    35.百度云语音识别接口使用及PyAudio语音识别模块安装
    34.TCP非阻塞连接及套接字异常处理丶端口扫描脚本
    33.TCP协议概念/scapy模块doos攻击
    32.网络编程TCP/UDP服务
    python递归函数实现阶乘函数
    用户不在sudoers文件中怎么办,ziheng is not in the sudoers file解决方法
    SecureCRT连接阿里云ECS服务器,经常掉线的解决方案
  • 原文地址:https://www.cnblogs.com/mogeko/p/11254822.html
Copyright © 2020-2023  润新知