zoukankan      html  css  js  c++  java
  • Codeforces Round #317 div2 E div1 C CNF 2 (图论,匹配)

     

      CNF 2  

    'In Boolean logic, a formula is in conjunctive normal form (CNF) or clausal normal form if it is a conjunction of clauses, where a clause is a disjunction of literals' (cited from https://en.wikipedia.org/wiki/Conjunctive_normal_form)

    In the other words, CNF is a formula of type , where & represents a logical "AND" (conjunction), represents a logical "OR" (disjunction), and vij are some boolean variables or their negations. Each statement in brackets is called a clause, and vij are called literals.

    You are given a CNF containing variables x1, ..., xm and their negations. We know that each variable occurs in at most two clauses (with negation and without negation in total). Your task is to determine whether this CNF is satisfiable, that is, whether there are such values of variables where the CNF value is true. If CNF is satisfiable, then you also need to determine the values of the variables at which the CNF is true.

    It is guaranteed that each variable occurs at most once in each clause.

    Input

    The first line contains integers n and m (1 ≤ n, m ≤ 2·105) — the number of clauses and the number variables, correspondingly.

    Next n lines contain the descriptions of each clause. The i-th line first contains first number ki (ki ≥ 1) — the number of literals in the i-th clauses. Then follow space-separated literals vij (1 ≤ |vij| ≤ m). A literal that corresponds to vij is x|vij| either with negation, if vij is negative, or without negation otherwise.

    Output

    If CNF is not satisfiable, print a single line "NO" (without the quotes), otherwise print two strings: string "YES" (without the quotes), and then a string of m numbers zero or one — the values of variables in satisfying assignment in the order from x1 to xm.

    一开始想成了网络流或者二分图匹配,但是增广的时间复杂度太高了。

    不过方法是类似的,首先,如果一个变量只出现一种形式,那么就让这个变量出现过的clause为真

    ,这些变量和对应的clause就可以排除了。如果一个变量出现了两次,那么就把变量var看成是一条边,把clasue看成是一个点

    在出现!var的clause U和出现var的clause V直接连一条边,选择var的值就转化成了决定边的方向

    规定一个clasue有至少一个入边的时候表示clause为真。贪心,之前已经排除掉的clasue U中出现的还没有决定的变量var都可以当作是对U为假,

    那么出现!var的V就全部为真了,这些V也可以排除了,所以可以用bfs或dfs完成这个过程。这是一个连通分量的情况。

    对于还没有考虑过的连通分量,如果是一颗树,那么一定不行,因为每次删除只有一条入边的叶子,最后一个会剩下一个点无法使它为真。

    是树即无环,因此用dfs找环,如果找到,那么将环中点删去,更新答案,重复bfs增广一个连通分量的操作。如果没找到,那么就输出NO。

    因为var和clasue最多被标记一次所以时间复杂是O(n+m)。线性复杂度都跑了405ms,网络流或者二分图匹配怎么可能不T

    #include<bits/stdc++.h>
    using namespace std;
    
    const int maxn = 2e5+5;
    
    #define PB push_back
    vector<int> adj[maxn][2];
    vector<int> G[maxn],id[maxn];
    char ans[maxn];
    bool ok[maxn];
    
    int n,m;
    
    queue<int> q;
    
    #define Add(x)
    ok[x] = true;
    q.push(x)
    
    void bfs()
    {
        while(q.size()){
            int u = q.front(); q.pop();
            for(int i = 0, M =  G[u].size(); i < M; i++){
                int v = G[u][i];
                if(ok[v]) continue;
                Add(v);
                int var = id[u][i];
                ans[var] = (v == adj[var][1][0])+'0';
            }
        }
    }
    
    bool vis[maxn];
    bool dfs(int u,int pvar)
    {
        if(vis[u]) return true;
        vis[u] = true;
        for(int i = 0, M = G[u].size(); i < M; i++){
            int v = G[u][i];
            int var = id[u][i];
            if(var == pvar) continue;
            if(dfs(v,var)){
                Add(u);
                ans[var] = (u == adj[var][1][0])+'0';
                return true;
            }
        }
        return false;
    }
    
    int main()
    {
        //freopen("in.txt","r",stdin);
        scanf("%d%d",&n,&m);
        for(int i = 0; i < n; i++){
            int k; scanf("%d",&k);
            int v;
            while(k--){
                scanf("%d",&v);
                int fg = v;
                if(v<0) v = -v;
                adj[--v][fg>0].PB(i);
            }
        }
    
        for(int i = 0; i < m; i++){
            if(adj[i][0].empty()){
                for(int j = 0,M = adj[i][1].size(); j < M; j++) {
                    Add(adj[i][1][j]);
                }
                ans[i] = '1';
            }else if(adj[i][1].empty()){
                for(int j = 0,M = adj[i][0].size(); j < M; j++) {
                    Add(adj[i][0][j]);
                }
                ans[i] = '0';
            }else {
                int u = adj[i][0][0], v = adj[i][1][0];
                G[u].PB(v); id[u].PB(i);
                G[v].PB(u); id[v].PB(i);
            }
        }
        for(int i = 0; i < n; i++){
            bfs();
            if(ok[i]) continue;
            if(!dfs(i,-1)){
                puts("NO");
                return 0;
            }
        }
    
        puts("YES");
        for(int i = 0; i < m; i++){
            if(ans[i]) putchar(ans[i]);
            else putchar('0');
        }
        return 0;
    }
  • 相关阅读:
    howtoautomateyouriphoneappbuildswithhudson
    buildingiphoneappswithhudsonpart2
    Linux常用命令全集
    介绍
    Linux文件查找命令find,xargs详述
    Tomcat for Mac OS
    Jenkins在Mac平台安裝
    Linux下的shell与make
    buildingiosappsforovertheairadhocdistribution
    linux下u盘的使用
  • 原文地址:https://www.cnblogs.com/jerryRey/p/4752502.html
Copyright © 2011-2022 走看看