zoukankan      html  css  js  c++  java
  • 题解 P4782 【【模板】2-SAT 问题】

    题目链接:Link

    Solution

    将每一个点(x_i)拆成(2i)(2i+1)(2i)表示(x_i)的假状态,(2i+1)表示(x_i)的真状态),问题就化为了从2n个状态中选出原来的每一个(x_i)的一个状态。

    这有什么用呢?别急,先举个栗子,“1 1 3 0”就可以用有向边 2->6 7->3 来表示,什么意思呢?2表示1为false,根据规则可得3一定为false(也就是6),7表示3为true,根据规则可得1一定为true。那么就可以对这个有向图的每个连通分量(不一定是强连通的)进行dfs处理,并加上剪枝、回溯就行了,细节见代码。

    注意:随机数据时dfs 2-SAT跑得很快,但对于大规模的数据,最好还是使用Tarjan求2-SAT

    // luogu-judger-enable-o2
    #include<cstdio>
    #include<vector>
    using namespace std;
    template<typename T> inline void read(T& t)
    {
        t=0; bool f=false; char ch;
        while(ch=getchar(),!((ch>='0'&&ch<='9')||ch=='-'));
        if(ch=='-') f=true,ch=getchar();
        t=ch-'0';
        while(ch=getchar(),ch>='0'&&ch<='9') t=t*10+ch-'0';
        if(f) t=-t;
    }
    template<typename T,typename... Args> inline void read(T& t,Args&... args) { read(t); read(args...); }
    const int maxn=1e6+7;
    struct TwoSAT
    {
        int n;
        vector<int> G[maxn*2];
        bool mark[maxn*2];
        int S[maxn*2],c;
        bool dfs(int x)
        {
            if(mark[x^1]) return false;
            if(mark[x]) return true;
            mark[x]=true;
            S[c++]=x;
            for(int i=0;i<G[x].size();i++)
                if(!dfs(G[x][i])) return false;
            return true;
        }
        //x = xval or y = yval
        void add_clause(int x,int xval,int y,int yval)
        {
            x=x*2+xval;
            y=y*2+yval;
            G[x^1].push_back(y);
            G[y^1].push_back(x);
            //一个不满足一定能推出另一个满足
        }
        bool solve()
        {
            for(int i=0;i<n*2;i+=2)
                if(!mark[i]&&!mark[i+1])//没被访问过
                {
                    c=0;
                    if(!dfs(i))
                    {
                        while(c>0) mark[S[--c]]=false;
                        //回溯
                        if(!dfs(i+1)) return false;
                        //true/false都不行那就无药可救了
                    }
                }
            return true;
        }
    }Ago;
    int m,x,xval,y,yval;
    int main()
    {
    #ifdef local
        freopen("pro.in","r",stdin);
    #endif
        read(Ago.n,m);
        while(m-->0)
        {
            read(x,xval,y,yval);
            Ago.add_clause(x,xval,y,yval);
        }
        if(!Ago.solve()) printf("IMPOSSIBLE
    ");
        else
        {
            printf("POSSIBLE
    ");
            for(int i=1;i<=Ago.n;i++)
                if(Ago.mark[i<<1]) printf("0 ");
                else printf("1 ");
            printf("
    ");
        }
        return 0;
    }
    
  • 相关阅读:
    Oracle Drop表并未直接删除 drop table xx purge
    Notepad++使用
    Python使用MySQL数据库
    考驾照科目二科目三要点记录
    Linux中权限(r、w、x)对于目录与文件的意义
    linux之expr命令
    linux下scp
    数字货币和区块链联系
    网站
    关于linux 编程
  • 原文地址:https://www.cnblogs.com/happyZYM/p/11379603.html
Copyright © 2011-2022 走看看