zoukankan      html  css  js  c++  java
  • (转)2-SAT小结

    2-sat小结


    原文作者:老K
    原文传送门


    2-sat是什么

    一类问题是这样的:

    (两个符号的意思 (lor or,land and)

    有n个布尔变量,现在对它们做出限制,比如(a_i=1,a_i lor a_j=1),求一组可行的解。

    假设限制元素最多的限制限制了k个元素,这个问题就被称为k-sat问题。

    可以证明(然而我不会),(k>2)时是NPC的。


    基础想法

    把每个变量(a_i)拆成2个点(i_0,i_1),表示它为1或0

    每个变量就变成了一个集合。要求在每个集合里选一个元素,满足所有限制。

    然后有向边(<u,v>)表示若选u则选v。

    (a_i=1)型限制

    (<i_0,i_1>)

    (a_i=0)则反过来。

    (a_i=a_j)限制

    (<i_0,j_0><j_0,i_0><i_1,j_1><j_1,i_1>)

    (a_i ot=a_j)限制

    (<i_0,j_1><j_0,i_1><i_1,j_0><j_1,i_0>)

    (a_ilor a_j=1)或者(a_iland a_j =0)限制

    对于前者,连(<i_0,j_1><j_0,i_1>)

    后者显然是交换0,1

    (a_ilor a_j=0)或者(a_iland a_j =1) 限制

    拆开,转化为(a_i=1)型限制

    其实都很显然是吧。


    算法1

    有一个很显然的结论:设(u')表示u所在集合的另一个元素。

    如果存在(<u,v>)则一定存在(<v',u'>)(对称性)

    那么我们可以按照顺序枚举每一个布尔变量。

    假设它是0,然后沿着单向边计算它影响的元素,如果没有问题就是0,并且计算它影响的元素的值。

    否则假设它是1,如果没问题就是1,并且计算它影响元素的值。

    否则无解。

    由于对称性以及边是单向的,可以证明这个算法的正确性。(或者感性理解)

    时间复杂度(O(nm))其中n是点数m是边数。

    当然0,1的顺序是可以交换的。

    这个算法可以求出字典序最小的解。

    在算之前,按照每个变量0,1编号较小值从小到大排序。

    然后枚举要先枚举每个变量编号较小的那个点。


    算法2

    有没有更快的算法呢?

    当然是有的。

    我们可以先tarjan 强连通分量缩点。

    那么同一个强连通分量里,点的选择情况是一样的(选则都选,不选则都不选)。

    由对称性可知如果(u,v)在同一个强连通分量里,那么(u',v')也在同一个强连通分量里。

    如果(u,u')在同一个强连通分量里,那么无解。

    否则考虑,缩完点后得到了一个DAG,由两个边相反的DAG组成。

    我们可以通过拓扑排序,每次找一个没有出边的分量,如果它里面点的对称点没有被选择,那么执行如下操作:

    选择它里面的所有点。

    把它扔掉。(把所有到它的边断掉)

    可以发现是对的。

    时间复杂度(O(n+m)),通常用来计算可行解或者判断可行性。


    算法3(黑科技)

    不难发现,在缩完点后,如果u能到达v,那么在tarjan 中,u一定在v之后被找到。

    于是我们可以在拓扑排序中,每次取能取的,编号最小的分量,

    显然,如果一个分量(u)和分量(v)是对称的,那么如果(u<v),会选择u,否则会选择v。

    那么就不用重建图拓扑排序了。

    常数优化/代码量优化效果还是很明显的。


    例题

    满汉全席

    模板题。


    NOI 2017 Day2T1 游戏

    之所以用UOJ是因为ex test巨强无比。

    发现有一些地图是X,3种都可以,但是个数不超过8.

    我们可以把X转化为A或B。

    枚举每个X转化为A还是B,跑2-SAT就可以了。

    /*
    Authlor: CNYALI_LK
    LANG: C++
    PROG: 317.cpp
    Mail: cnyalilk@vip.qq.com
    */
    #include<bits/stdc++.h>
    #define debug(...) fprintf(stderr,__VA_ARGS__)
    #define DEBUG printf("Passing [%s] in LINE %d
    ",__FUNCTION__,__LINE__)
    #define Debug debug("Passing [%s] in LINE %d
    ",__FUNCTION__,__LINE__)
    #define all(x) x.begin(),x.end()
    using namespace std;
    const double eps=1e-8;
    const double pi=acos(-1.0);
    typedef long long ll;
    typedef pair<int,int> pii;
    template<class T>int chkmin(T &a,T b){return a>b?a=b,1:0;}
    template<class T>int chkmax(T &a,T b){return a<b?a=b,1:0;}
    template<class T>T sqr(T a){return a*a;}
    template<class T>T mmin(T a,T b){return a<b?a:b;}
    template<class T>T mmax(T a,T b){return a>b?a:b;}
    template<class T>T aabs(T a){return a<0?-a:a;}
    #define min mmin
    #define max mmax
    #define abs aabs
    int read(){
        int s=0,base=1;
        char c;
        while(!isdigit(c=getchar()))if(c=='-')base=-base;
        while(isdigit(c)){s=s*10+(c^48);c=getchar();}
        return s*base;
    }
    char rc(){
        char c;
        while(!isalpha(c=getchar()));
        return c;
    }
    void rs(char *s){
        while(!isalpha(*s=getchar()))++s;
        while(isalpha(*s))*(++s)=getchar();
        *s=0;
    }
    char WriteIntBuffer[1024];
    template<class T>void write(T a,char end){
        int cnt=0,fu=1;
        if(a<0){putchar('-');fu=-1;}
        do{WriteIntBuffer[++cnt]=fu*(a%10)+'0';a/=10;}while(a);
        while(cnt){putchar(WriteIntBuffer[cnt]);--cnt;}
        putchar(end);
    }
    struct _limit{
        int a,b,ax,bx;
    };
    _limit a[102424];
    char s[102424];
    _limit limit(){
        _limit x;
        x.a=read()-1;
        x.ax=rc();
        x.b=read()-1;
        x.bx=rc();
        return x;
    }
    int h[102424];
    int beg[102424],to[233333],lst[233333],dfn[102423],low[102423],no[102423]; 
    int blocks,e,t;
    void add(int u,int v){
        to[++e]=v;
        lst[e]=beg[u];
        beg[u]=e;
    }
    int stk[102424],*top=stk;
    void dfs(int x){
        dfn[x]=low[x]=++t;
        *(++top)=x;
        flor(int i=beg[x];i;i=lst[i])
            if(dfn[to[i]]){
                if(!no[to[i]])
                    chkmin(low[x],dfn[to[i]]);
            }else{
                dfs(to[i]);
                chkmin(low[x],low[to[i]]);
            }
        if(dfn[x]==low[x]){
            ++blocks;
            do{
                no[*(--top+1)]=blocks;
            }while(*(top+1)!=x);
        }
    }
    
    int main(){
    #ifdef cnyali_lk
        freopen("317.in","r",stdin);
        freopen("317.out","w",stdout);
    #endif
        int n,d,m;
        n=read();
        d=read();
        rs(s);
        int xs=0;
        flor(int i=0;i<n;++i){
            if(s[i]=='x'){h[xs]=i;++xs;}
        }
        m=read();
        flor(int i=1;i<=m;++i){
            a[i]=limit();
        }
        int un=1<<d,u,v;
        flor(int j=0;j<un;++j){
            flor(int i=0;i<d;++i)s[h[i]]='a'+!!(j&(1<<i));
            flor(int i=0;i<n+n;++i)beg[i]=dfn[i]=low[i]=no[i]=0;
            blocks=e=t=0;   
            flor(int i=1;i<=m;++i){
                if(a[i].ax-'A'==s[a[i].a]-'a')continue;
                if(a[i].bx-'A'==s[a[i].b]-'a'){
                    int u=a[i].a*2+(a[i].ax-'A'-(a[i].ax-'A'>=s[a[i].a]-'a'));
                    add(u,u^1);
                }
                else{
                    int u=a[i].a*2+(a[i].ax-'A'-(a[i].ax-'A'>=s[a[i].a]-'a'));
                    int v=a[i].b*2+(a[i].bx-'A'-(a[i].bx-'A'>=s[a[i].b]-'a'));
                    add(u,v);
                    add(v^1,u^1);
                }
            }
            flor(int i=0;i<n+n;++i)if(!dfn[i])dfs(i);
            int ok=1;
            flor(int i=0;i<n;++i)if(no[i<<1]==no[i<<1|1])ok=0;
            if(ok){
                flor(int i=0;i<n;++i){
                    int ans=(no[i<<1]>no[i<<1|1]);
                    ans+=(ans>=s[i]-'a');
                    putchar(ans+'A');
                }
                return 0;
            }
        }
        printf("-1
    ");
        return 0;
    }
    
    

    ({Hugecolor{Gold}{老K太强了QAQ}})

  • 相关阅读:
    loj 3285 「USACO 2020 US Open Platinum」Circus
    UOJ Round #19 简要题解
    AtCoder Grand Contest 043 简要题解
    JOISC 2020 部分题目简要题解
    HNOI 2019 简要题解
    十二省联考 2019 简要题解
    九省联考 2018 简要题解
    loj 6391 「THUPC2018」淘米神的树 / Tommy
    loj 6703 小 Q 的序列
    Goodbye Jihai 部分题目简要题解
  • 原文地址:https://www.cnblogs.com/enceladus-return0/p/9594098.html
Copyright © 2011-2022 走看看