zoukankan      html  css  js  c++  java
  • 2-SAT

        前天打了一场codeforces,发现c题是一道2-sat题,然而我并不会打,于是就爆炸.于是现在就仔细学一发2-sat.

        首先把每个变量拆成2个点,一个表示它为0,一个表示它为1.把所有限制条件拆成"如果i的值是a,j的值必须是b"这样的条件,然后从表示i的值为a的点向表示j的值为b的点连一条边,再从表示j的值为!b的点向表示i的值为!a的点连一条边.

        然后跑tarjan缩点.跑完之后如果对于某个变量,表示它为0的点和表示它为1的点在同一个连通分量了就无解,否则肯定有解.

        然后接下来网上的一些写法需要对缩过点的图进行拓扑排序,然而我从zrf大佬的博客得知tarjan出来的顺序就是反过来的拓扑序,于是就可以省去这个步骤,用一些更高妙的方法求.

        对于每个变量,如果它为0的点所在的连通分量的编号大于它为1的点所在的连通分量的编号,该变量的值就为1,否则就为0.

        用这个方法打了一遍codeforces875C,比我比赛时从网上乱搞来的2-sat板子短多了.

        875C的代码在下面.

    #include<cstdio>
    #include<vector>
    #include<stack>
    using namespace std;
    const int maxn=200000;
    vector<int> a[maxn+10];
    int n,m;
    vector<int> G[maxn+10];
    bool value[maxn+10];
    int dfn[maxn+10],low[maxn+10],scc[maxn+10],scc_cnt,dfs_cnt;
    stack<int> S;
    int ans;
    void dfs(int x){
        dfn[x]=low[x]=++dfs_cnt; S.push(x);
        for(int i=0;i<G[x].size();++i){
            int e=G[x][i];
            if(!dfn[e]){
                dfs(e); low[x]=min(low[x],low[e]);
            }else if(!scc[e]) low[x]=min(low[x],dfn[e]);
        }
        if(low[x]==dfn[x]){
            ++scc_cnt;
            for(;;){
                int t=S.top(); S.pop();
                scc[t]=scc_cnt; if(x==t) break;
            }
        }
    }
    int main(){
        scanf("%d%d",&n,&m);
        for(int i=1;i<=n;++i){
            int l; scanf("%d",&l); a[i].resize(l);
            for(int j=0;j<l;++j) scanf("%d",&a[i][j]);
        }
        for(int i=1;i<n;++i){
            for(int j=0;j<a[i].size();++j)
                if(j>=a[i+1].size()){
                    printf("No
    "); return 0;
                }else if(a[i][j]!=a[i+1][j]){
                    int l=a[i][j],r=a[i+1][j];
                    if(l<r){
                        G[l<<1].push_back(r<<1);
                        G[(r<<1)-1].push_back((l<<1)-1);
                    }else{
                        G[l<<1].push_back((l<<1)-1);
                        G[(r<<1)-1].push_back(r<<1);
                    }
                    break;
                }
        }    
        for(int i=1;i<=m<<1;++i) if(!dfn[i]) dfs(i);
        for(int i=1;i<=m;++i) if(scc[i<<1]==scc[(i<<1)-1]){
            printf("No
    "); return 0;
        }else ans+=(value[i]=(scc[i<<1]>scc[(i<<1)-1]));
        printf("Yes
    %d
    ",ans);
        for(int i=1;i<=m;++i) if(value[i]) printf("%d ",i);
        return 0;
    }
  • 相关阅读:
    LintCode2016年8月22日算法比赛----骰子求和
    LintCode2016年8月22日算法比赛----平面列表
    LintCode2016年8月22日算法比赛----将数组重新排序以构造最小值
    LintCode2016年8月22日算法比赛----克隆二叉树
    Leetcode算法比赛----Longest Absolute File Path
    Leetcode算法比赛----First Unique Character in a String
    vue运行报错Error: listen EADDRNOTAVAIL 192.168.1.105:8080
    vue使用lrz插件压缩图片
    <input type="file">原型难看
    vue创建全局变量以及全局方法
  • 原文地址:https://www.cnblogs.com/jxcakak/p/7687272.html
Copyright © 2011-2022 走看看