2-SAT存模板

只判定是否有解

例题:http://acm.hdu.edu.cn/showproblem.php?pid=3622

O(|V||E|) 方法


#include <cstdio>
#include <cstdlib>
#include <algorithm>
#include <utility>
#include <cstring>
#include <map>
#include <climits>
#include <queue>
#include <cmath>
#include <set>

using namespace std;

typedef long long LL;
typedef unsigned long long ULL;
typedef unsigned UI;

const int MAXN(201);
const int MAXE(40001);
const int MAXK(100010);
const int MAXL(10);
const int MAXC(2);
const int INF((INT_MAX - 1) / 2);
const int F(0);

template<typename T>
inline bool checkmax(T &a, const T &b) {
    return b > a ? ((a = b), true) : false;
}

template<typename T>
inline bool checkmin(T &a, const T &b) {
    return b < a ? ((a = b), true) : false;
}

template<typename T>
inline T ABS(T a) {
    return a < 0 ? -a : a;
}

double dis(double x1, double y1, double x2, double y2) {
    return sqrt((y2 - y1)*(y2 - y1) + (x2 - x1)*(x2 - x1));
}

double dis2(double x1, double y1, double x2, double y2) {
    return (y2 - y1)*(y2 - y1) + (x2 - x1)*(x2 - x1);
}

struct E {
    int u, v;
    E *next;
    E(int u_, int v_, int w_, E *n_) :u(u_), v(v_), next(n_) {}
    E(){}
};

double disArr[MAXN][MAXN];
double coor[MAXN][2];
int label[MAXN];
E *first[MAXN];
E edge[MAXE], *rear;
int st[MAXN], stn;

void init(int n) {
    memset(first, 0, sizeof(first[0])*(n + 1));
    rear = edge;
}

void addEdge(int u, int v) {
    rear->u = u;
    rear->v = v;
    rear->next = first[u];
    first[u] = rear++;
}

bool conflict(int a, int b, double m) {
    return disArr[a][b] < m;
}

bool dfs(int u) {
    int ind = u >> 1, fl = u & 1;
    if (label[ind] == -1)
        label[ind] = fl;
    else
        return label[ind] == fl;
    st[stn++] = ind;
    for (E *i = first[u]; i; i = i->next) {
        if (!dfs(i->v)) return false;
    }
    return true;
}

bool check(double m, int n) {
    init(n * 2);
    for (int i = 0; i < n; ++i) label[i] = -1;
    for (int i = 0; i < n; ++i)
        for (int j = i + 1; j < n; ++j) {
            int ti = i * 2, tj = j * 2;
            for (int k1 = 0; k1 < 2; ++k1)
                for (int k2 = 0; k2 < 2; ++k2) {
                    int a = ti | k1, b = tj | k2;
                    if (conflict(a, b, m)) {
                        addEdge(a, b ^ 1);
                        addEdge(b, a ^ 1);
                    }
                }
        }
    for (int i = 0; i < n; ++i) {
        if (label[i] != -1) continue;
        stn = 0;
        if (!dfs(i * 2)){
            while (stn > 0) label[st[--stn]] = -1;
            if (!dfs(i * 2 + 1)) return false;
        }
    }
    return true;
}

int main() {
    int n;
    while (~scanf("%d", &n)) {
        double low = 0, high = 0;
        init(n * 2);
        for (int i = 0; i < n; ++i) {
            int ti = i * 2;
            scanf("%lf%lf", coor[ti], coor[ti] + 1);
            scanf("%lf%lf", coor[ti+1], coor[ti+1] + 1);
        }
        for (int i = 0; i < 2 * n; ++i)
            for (int j = i + 1; j < 2 * n; ++j) {
                disArr[i][j] = disArr[j][i] = dis2(coor[i][0], coor[i][1], coor[j][0], coor[j][1]);
                checkmax(high, disArr[i][j]);
            }
        while (high - low > 1e-6) {
            double m = (high + low) / 2;
            if (check(m, n))
                low = m;
            else
                high = m;
        }
        printf("%.2f\n", sqrt((high + low) / 2)/2);
    }
    return 0;
}

O(|V|+|E|) 方法,此题题数据弱,不如上一个方法快


#include <cstdio>
#include <cstdlib>
#include <algorithm>
#include <utility>
#include <cstring>
#include <map>
#include <climits>
#include <queue>
#include <cmath>
#include <set>
#include <stack>

using namespace std;

typedef long long LL;
typedef unsigned long long ULL;
typedef unsigned UI;

const int MAXN(201);
const int MAXE(40001);
const int MAXK(100010);
const int MAXL(10);
const int MAXC(2);
const int INF((INT_MAX - 1) / 2);
const int F(0);

template<typename T>
inline bool checkmax(T &a, const T &b) {
    return b > a ? ((a = b), true) : false;
}

template<typename T>
inline bool checkmin(T &a, const T &b) {
    return b < a ? ((a = b), true) : false;
}

template<typename T>
inline T ABS(T a) {
    return a < 0 ? -a : a;
}

double dis(double x1, double y1, double x2, double y2) {
    return sqrt((y2 - y1)*(y2 - y1) + (x2 - x1)*(x2 - x1));
}

double dis2(double x1, double y1, double x2, double y2) {
    return (y2 - y1)*(y2 - y1) + (x2 - x1)*(x2 - x1);
}

struct E {
    int u, v;
    E *next;
    E(int u_, int v_, int w_, E *n_) :u(u_), v(v_), next(n_) {}
    E(){}
};

double disArr[MAXN][MAXN];
double coor[MAXN][2];
int label[MAXN];
E *first[MAXN];
E edge[MAXE], *rear;

void init(int n) {
    memset(first, 0, sizeof(first[0])*(n + 1));
    rear = edge;
}

void addEdge(int u, int v) {
    rear->u = u;
    rear->v = v;
    rear->next = first[u];
    first[u] = rear++;
}

bool conflict(int a, int b, double m) {
    return disArr[a][b] < m;
}

int dfn[MAXN], anc[MAXN];
int scc[MAXN];
int dfsClo, sccCnt;
stack<int> st;

void dfs(int u) {
    dfn[u] = anc[u] = dfsClo++;
    st.push(u);
    for (E *i = first[u]; i; i = i->next) {
        int v = i->v;
        if (dfn[v] == -1) {
            dfs(v);
            checkmin(anc[u], anc[v]);
        }
        else {
            if (scc[v] == -1) {
                checkmin(anc[u], dfn[v]);
            }
        }
    }
    if (dfn[u] == anc[u]) {
        int t;
        do {
            t = st.top();
            st.pop();
            scc[t] = sccCnt;
        } while (t != u);
        ++sccCnt;
    }
}

void findScc(int n) {
    for (int i = 0; i < n; ++i) dfn[i] = anc[i] = scc[i] = -1;
    dfsClo = sccCnt = 0;
    for (int i = 0; i < n; ++i)
        if (dfn[i] == -1)
            dfs(i);
}

bool check(double m, int n) {
    init(n * 2);
    for (int i = 0; i < n; ++i) label[i] = -1;
    for (int i = 0; i < n; ++i)
        for (int j = i + 1; j < n; ++j) {
            int ti = i * 2, tj = j * 2;
            for (int k1 = 0; k1 < 2; ++k1)
                for (int k2 = 0; k2 < 2; ++k2) {
                    int a = ti | k1, b = tj | k2;
                    if (conflict(a, b, m)) {
                        addEdge(a, b ^ 1);
                        addEdge(b, a ^ 1);
                    }
                }
        }
    findScc(n * 2);
    for (int i = 0; i < n; ++i)
        if (scc[i * 2] == scc[i * 2 + 1])
            return false;
    return true;
}

int main() {
    int n;
    while (~scanf("%d", &n)) {
        double low = 0, high = 0;
        init(n * 2);
        for (int i = 0; i < n; ++i) {
            int ti = i * 2;
            scanf("%lf%lf", coor[ti], coor[ti] + 1);
            scanf("%lf%lf", coor[ti+1], coor[ti+1] + 1);
        }
        for (int i = 0; i < 2 * n; ++i)
            for (int j = i + 1; j < 2 * n; ++j) {
                disArr[i][j] = disArr[j][i] = dis2(coor[i][0], coor[i][1], coor[j][0], coor[j][1]);
                checkmax(high, disArr[i][j]);
            }
        while (high - low > 1e-6) {
            double m = (high + low) / 2;
            if (check(m, n))
                low = m;
            else
                high = m;
        }
        printf("%.2f\n", sqrt((high + low) / 2)/2);
    }
    return 0;
}

增加了与操作和异或操作后

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

#include <cstdio>
#include <cstdlib>
#include <algorithm>
#include <utility>
#include <cstring>
#include <map>
#include <climits>
#include <queue>
#include <cmath>
#include <set>
#include <stack>

using namespace std;

typedef long long LL;
typedef unsigned long long ULL;
typedef unsigned UI;

const int MAXN(2010);
const int MAXE(4000010);
const int MAXK(100010);
const int MAXL(10);
const int MAXC(2);
const int INF((INT_MAX - 1) / 2);
const int F(0);

template<typename T>
inline bool checkmax(T &a, const T &b) {
    return b > a ? ((a = b), true) : false;
}

template<typename T>
inline bool checkmin(T &a, const T &b) {
    return b < a ? ((a = b), true) : false;
}

template<typename T>
inline T ABS(T a) {
    return a < 0 ? -a : a;
}

double dis(double x1, double y1, double x2, double y2) {
    return sqrt((y2 - y1)*(y2 - y1) + (x2 - x1)*(x2 - x1));
}

double dis2(double x1, double y1, double x2, double y2) {
    return (y2 - y1)*(y2 - y1) + (x2 - x1)*(x2 - x1);
}

struct E {
    int u, v;
    E *next;
    E(int u_, int v_, int w_, E *n_) :u(u_), v(v_), next(n_) {}
    E(){}
};

E *first[MAXN];
E edge[MAXE], *rear;

void init(int n) {
    memset(first, 0, sizeof(first[0])*(n + 1));
    rear = edge;
}

void addEdge(int u, int v) {
    rear->u = u;
    rear->v = v;
    rear->next = first[u];
    first[u] = rear++;
}


int dfn[MAXN], anc[MAXN];
int scc[MAXN];
int dfsClo, sccCnt;
stack<int> st;

void dfs(int u) {
    dfn[u] = anc[u] = dfsClo++;
    st.push(u);
    for (E *i = first[u]; i; i = i->next) {
        int v = i->v;
        if (dfn[v] == -1) {
            dfs(v);
            checkmin(anc[u], anc[v]);
        }
        else {
            if (scc[v] == -1) {
                checkmin(anc[u], dfn[v]);
            }
        }
    }
    if (dfn[u] == anc[u]) {
        int t;
        do {
            t = st.top();
            st.pop();
            scc[t] = sccCnt;
        } while (t != u);
        ++sccCnt;
    }
}

void findScc(int n) {
    for (int i = 0; i < n; ++i) dfn[i] = anc[i] = scc[i] = -1;
    dfsClo = sccCnt = 0;
    for (int i = 0; i < n; ++i)
        if (dfn[i] == -1)
            dfs(i);
}

int label[MAXN];

bool check(int n) {
    findScc(n * 2);
    for (int i = 0; i < n; ++i)
        if (scc[i * 2] == scc[i * 2 + 1])
            return false;
    for (int i = 0; i < n; ++i) {
        label[i] = scc[i * 2] < scc[i * 2 + 1] ? 1 : 0;
    }
    return true;
}

int rec[MAXN][2];

void AND(int a, int b) {
    addEdge(a ^ 1, a);
    addEdge(b ^ 1, b);
}
void OR(int a, int b) {
    addEdge(a ^ 1, b);
    addEdge(b ^ 1, a);
}
void XOR(int a, int b) {
    addEdge(a ^ 1, b);
    addEdge(b, a ^ 1);
    addEdge(b ^ 1, a);
    addEdge(a, b ^ 1);
}
void EOR(int a, int b) {
    addEdge(a,b);
    addEdge(a^1, b^1);
    addEdge(b, a);
    addEdge(b^1, a^1);
}

int main() {
    int n, m;
    while (~scanf("%d%d", &n, &m)) {
        init(2 * n);
        int a, b, c;
        char op[5];
        for (int i = 0; i < m; ++i) {
            scanf("%d%d%d%s", &a, &b, &c, op);
            switch (op[0]) {
            case 'A':
                if (c) AND(a*2, b*2);
                else OR(a * 2 + 1, b * 2 + 1);
                break;
            case 'O':
                if (c) OR(a * 2, b * 2);
                else AND(a * 2 + 1, b * 2 + 1);
                break;
            case 'X':
                if (c) XOR(a * 2, b * 2);
                else EOR(a * 2 , b * 2 );
                break;
            }
        }
        printf("%s\n", check(n) ? "YES" : "NO");
    }
    return 0;
}

输出最小字典序方案

例题:http://acm.hdu.edu.cn/showproblem.php?pid=1814
只能用 O(|V||E|) 方法


#include <cstdio>
#include <cstdlib>
#include <algorithm>
#include <utility>
#include <cstring>
#include <map>
#include <climits>
#include <queue>
#include <cmath>
#include <set>

using namespace std;

typedef long long LL;
typedef unsigned long long ULL;
typedef unsigned UI;

const int MAXN(16010);
const int MAXE(40010);
const int MAXK(100010);
const int MAXL(10);
const int MAXC(2);
const int INF((INT_MAX - 1) / 2);
const int F(0);

template<typename T>
inline bool checkmax(T &a, const T &b) {
    return b > a ? ((a = b), true) : false;
}

template<typename T>
inline bool checkmin(T &a, const T &b) {
    return b < a ? ((a = b), true) : false;
}

template<typename T>
inline T ABS(T a) {
    return a < 0 ? -a : a;
}

double dis(double x1, double y1, double x2, double y2) {
    return sqrt((y2 - y1)*(y2 - y1) + (x2 - x1)*(x2 - x1));
}

double dis2(double x1, double y1, double x2, double y2) {
    return (y2 - y1)*(y2 - y1) + (x2 - x1)*(x2 - x1);
}

struct E {
    int u, v;
    E *next;
    E(int u_, int v_, int w_, E *n_) :u(u_), v(v_), next(n_) {}
    E() {}
};

int label[MAXN];
E *first[MAXN];
E edge[MAXE], *rear;
int st[MAXN], stn;

void init(int n) {
    memset(first, 0, sizeof(first[0])*(n + 1));
    rear = edge;
}

void addEdge(int u, int v) {
    rear->u = u;
    rear->v = v;
    rear->next = first[u];
    first[u] = rear++;
}

bool dfs(int u) {
    int ind = u >> 1, fl = u & 1;
    if (label[ind] == -1)
        label[ind] = fl;
    else
        return label[ind] == fl;
    st[stn++] = ind;
    for (E *i = first[u]; i; i = i->next) {
        if (!dfs(i->v)) return false;
    }
    return true;
}

bool check(int n) {
    for (int i = 0; i < n; ++i) label[i] = -1;
    for (int i = 0; i < n; ++i) {
        if (label[i] != -1) continue;
        stn = 0;
        if (!dfs(i * 2)) {
            while (stn > 0) label[st[--stn]] = -1;
            if (!dfs(i * 2 + 1)) return false;
        }
    }
    return true;
}

int main() {
    int n, m;
    while (~scanf("%d%d", &n, &m)) {
        init(2 * n);
        int a, b;
        for (int i = 0; i < m; ++i) {
            scanf("%d%d", &a, &b);
            --a;
            --b;
            addEdge(a, b ^ 1);
            addEdge(b, a ^ 1);
        }
        if (check(n)) {
            for (int i = 0; i < n; ++i)
                printf("%d\n", (2 * i | label[i]) + 1);
        }
        else
            printf("NIE\n");
    }
    return 0;
}

输出任意一组方案

例题:http://poj.org/problem?id=3648
需注意的是此题要输出合法赋值取反后的结果


#include <cstdio>
#include <cstdlib>
#include <algorithm>
#include <utility>
#include <cstring>
#include <map>
#include <climits>
#include <queue>
#include <cmath>
#include <set>
#include <stack>

using namespace std;

typedef long long LL;
typedef unsigned long long ULL;
typedef unsigned UI;

const int MAXN(5000);
const int MAXE(10010);
const int MAXK(100010);
const int MAXL(10);
const int MAXC(2);
const int INF((INT_MAX - 1) / 2);
const int F(0);

template<typename T>
inline bool checkmax(T &a, const T &b) {
    return b > a ? ((a = b), true) : false;
}

template<typename T>
inline bool checkmin(T &a, const T &b) {
    return b < a ? ((a = b), true) : false;
}

template<typename T>
inline T ABS(T a) {
    return a < 0 ? -a : a;
}

double dis(double x1, double y1, double x2, double y2) {
    return sqrt((y2 - y1)*(y2 - y1) + (x2 - x1)*(x2 - x1));
}

double dis2(double x1, double y1, double x2, double y2) {
    return (y2 - y1)*(y2 - y1) + (x2 - x1)*(x2 - x1);
}

struct E {
    int u, v;
    E *next;
    E(int u_, int v_, int w_, E *n_) :u(u_), v(v_), next(n_) {}
    E(){}
};

E *first[MAXN];
E edge[MAXE], *rear;

void init(int n) {
    memset(first, 0, sizeof(first[0])*(n + 1));
    rear = edge;
}

void addEdge(int u, int v) {
    rear->u = u;
    rear->v = v;
    rear->next = first[u];
    first[u] = rear++;
}


int dfn[MAXN], anc[MAXN];
int scc[MAXN];
int dfsClo, sccCnt;
stack<int> st;

void dfs(int u) {
    dfn[u] = anc[u] = dfsClo++;
    st.push(u);
    for (E *i = first[u]; i; i = i->next) {
        int v = i->v;
        if (dfn[v] == -1) {
            dfs(v);
            checkmin(anc[u], anc[v]);
        }
        else {
            if (scc[v] == -1) {
                checkmin(anc[u], dfn[v]);
            }
        }
    }
    if (dfn[u] == anc[u]) {
        int t;
        do {
            t = st.top();
            st.pop();
            scc[t] = sccCnt;
        } while (t != u);
        ++sccCnt;
    }
}

void findScc(int n) {
    for (int i = 0; i < n; ++i) dfn[i] = anc[i] = scc[i] = -1;
    dfsClo = sccCnt = 0;
    for (int i = 0; i < n; ++i)
        if (dfn[i] == -1)
            dfs(i);
}

int label[MAXN];

bool check(int n) {
    findScc(n * 2);
    for (int i = 0; i < n; ++i)
        if (scc[i * 2] == scc[i * 2 + 1])
            return false;
    for (int i = 0; i < n; ++i) {
        label[i] = scc[i * 2] < scc[i * 2 + 1] ? 0 : 1;
    }
    return true;
}

int main() {
    int n, m;
    while (~scanf("%d%d", &n, &m)) {
        init(2 * n);
        int a, b;
        char ca, cb;
        for (int i = 0; i < m; ++i) {
            scanf("%d %c %d %c", &a, &ca, &b, &cb);
            int ta = 2*a + (ca == 'h' ? 0 : 1);
            int tb = 2*b + (cb == 'h' ? 0 : 1);
            addEdge(ta, tb ^ 1);
            addEdge(tb, ta ^ 1);
        }
        addEdge(1, 0);
        if (check(n)) {
            bool pr(false);
            for (int i = 1; i < n; ++i) {
                if (pr) printf(" ");
                else
                    pr = true;
                printf("%d", i);
                printf("%c", ((label[i]^1) == 0 ? 'h' : 'w'));
            }
            printf("\n");
        }
        else
            printf("bad luck\n");
    }
    return 0;
}

算法的正确性简要分析

这里对赵爽在《2-SAT解法浅析》中的算法给出宏观的证明思路。
如果将 x x看做两个独立的符号,则一个合法方案应满足两个条件。
1. sat条件;
2. x x取值相异。

对于染色算法的正确性来说证明的思路大致如下:

假设染红色表示该点取1,染蓝色表示该点取0。首先由于我们的建图方法,只要保证不存在这样的 uv ,使得 u 染红色,v染蓝色就可以保证条件1满足。

首先证明了存在一种染色方案,其满足条件2。

进一步证明该染色方案满足条件1。

就完成了整个证明。

添加了与操作和异(同)或操作后,可以仿照类似的思路,证明原算法仍然适用。

进一步根据对称性我们还可以证明,在原图中,按照拓扑逆序染色,被染蓝色的点,其祖先也早被染成蓝色,因此就得到了不用反向建图求具体方案的算法(对加入了与操作和异(同)或操作后的情况也成立)。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值