基础但是很重要的2-sat POJ 3678

http://poj.org/problem?id=3678

题目大意:就是给你n个点,m条边,每个点都可以取值为0或者1,边上都会有一个符号op(op=xor or and三种)和一个权值c。然后问你如何选择每个点的值,才能让所有点都满足x[i] op x[j] = c

思路:

这题学会了好多东西哇,至少我明白了xor,or,and这些关系如何建图拉

//看看会不会爆int!数组会不会少了一维!
//取物问题一定要小心先手胜利的条件
#include<cstdio>
#include<cstring>
#include<vector>
#include<map>
#include<set>
#include<queue>
#include<algorithm>
#include<iostream>
#include<utility>
#include<stack>
#include<stdlib.h>
#include<time.h>
#include<cmath>
using namespace std;
#pragma comment(linker,"/STACK:102400000,102400000")
#define LL long long
#define ALL(a) a.begin(), a.end()
#define pb push_back
#define mk make_pair
#define fi first
#define se second
#define haha printf("haha
")
const int maxn = 10000 + 5;
struct Tarjan{
    int n;
    int pre[maxn*2], low[maxn*2], sccno[maxn*2], dfstime, scc_cnt;
    stack<int> s;
    vector<int> G[maxn*2];
    void init(int n){
        this->n = n;
        for (int i = 0; i <= n * 2; i++) G[i].clear();
        while (!s.empty()) s.pop();
    }
    void add_edge(int x, int y){
        G[x].push_back(y);
    }
    void dfs(int u){
        pre[u] = low[u] = ++dfstime;
        int len = G[u].size();
        s.push(u);
        for (int i = 0; i < len; i++){
            int v = G[u][i];
            if (pre[v] == -1){
                dfs(v);
                low[u] = min(low[u], low[v]);
            }
            else if (!sccno[v]){///下面为什么是pre[v]而不是low[v](两者都可以)
                low[u] = min(low[u], pre[v]);///因为环装最后总会变回来,一样的
            }
        }
        if (low[u] == pre[u]){
            scc_cnt++;
            while (true){
                int x = s.top(); s.pop();
                sccno[x] = scc_cnt;
                if (x == u) break;
            }
        }
        return ;
    }

    bool solve(){///看情况修改solve
        memset(pre, -1, sizeof(pre));
        memset(low, 0, sizeof(low));
        memset(sccno, 0, sizeof(sccno));
        dfstime = scc_cnt = 0;
        for (int i = 0; i < 2 * n; i++){
            if (pre[i] == -1) dfs(i);
        }
        for (int i = 0; i < 2 * n; i += 2){///如果两个点在同一个环里面,那么就返回false
            if (sccno[i] == sccno[i + 1]) return false;
        }
        return true;
    }
};
Tarjan tar;
int n, m;

int main(){
    while (scanf("%d%d", &n, &m) != EOF){
        char ch[5];
        tar.init(n);
        for (int i = 0; i < m; i++){
            int a, b, c; scanf("%d%d%d%s", &a, &b, &c, ch);
            a = a * 2, b = b * 2;///刚开始都是0
            if (ch[0] == 'A'){
                if (c == 1){///a
                    tar.add_edge(a ^ 1, a);
                    tar.add_edge(b ^ 1, b);
                }
                else {
                    tar.add_edge(a, b ^ 1);
                    tar.add_edge(b, a ^ 1);
                }
            }
            else if (ch[0] == 'O'){
                if (c == 1){
                    tar.add_edge(a ^ 1, b);
                    tar.add_edge(b ^ 1, a);
                }
                else {
                    tar.add_edge(a, a ^ 1);
                    tar.add_edge(b, b ^ 1);
                }
            }
            else if (ch[0] == 'X'){
                if (c == 1){
                    tar.add_edge(a, b ^ 1);
                    tar.add_edge(b ^ 1, a);
                    tar.add_edge(a ^ 1, b);
                    tar.add_edge(b, a ^ 1);
                }
                else {
                    tar.add_edge(a, b);
                    tar.add_edge(b, a);
                    tar.add_edge(a ^ 1, b ^ 1);
                    tar.add_edge(b ^ 1, a ^ 1);
                }
            }
        }
        if (tar.solve()) puts("YES");
        else puts("NO");
    }
    return 0;
}
View Code

具体可以看这个博客:http://www.hankcs.com/program/algorithm/poj-3678-katu-puzzle.html

原文地址:https://www.cnblogs.com/heimao5027/p/6591164.html