2sat

Posted DearDongchen

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了2sat相关的知识,希望对你有一定的参考价值。

之前做的两发

https://vjudge.net/problem/UVALive-3211

技术分享图片
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<cmath>
using namespace std;
const int maxn = 2007;
int t[maxn][2], n, tot, S[maxn*2], scnt;
bool mark[maxn*2];
struct Edge{
    int v, nxt;
    Edge(){}
    Edge(int v, int nxt):v(v), nxt(nxt){}
}edge[maxn*maxn*4];
int head[maxn*2];
void addedge(int u, int v){
    edge[tot] = Edge(v, head[u]);
    head[u] = tot++;
}
void conj(int x, int detx, int y, int dety){
    x = 2*x+detx;
    y = 2*y+dety;
    addedge(x^1, y);
    addedge(y^1, x);
}
void build(int mid){
    tot = 0;
    memset(head, -1, sizeof head);
    for(int i = 0; i < n; i++)
        for(int a = 0; a < 2; a++)
            for(int j = i+1; j < n; j++)
                for(int b = 0; b < 2; b++)
                    if(fabs(t[i][a]-t[j][b]) < mid)
                        conj(i, a^1, j, b^1);
    
}
bool dfs(int x){
    if(mark[x])
        return true;
    if(mark[x^1])
        return false;
    mark[x] = true;
    S[scnt++] = x;
    for(int i = head[x]; ~i; i = edge[i].nxt){
        int v = edge[i].v;
        if(!dfs(v))
            return false;
    }
    return true;
}
bool solve(){
    memset(mark, false, sizeof mark);
    for(int i = 0; i < 2*n; i+=2){
        if(!mark[i]&&!mark[i+1]){
            scnt = 0;
            if(!dfs(i)){
                for(int j = 0; j < scnt; j++){
                    mark[S[j]] = false;
                }
                scnt = 0;
                if(!dfs(i+1))
                    return false;
            }
        }
    }
    return true;
}
int main(){
    while(scanf("%d", &n)==1 && n){
        for(int i = 0; i < n; i++)
            for(int j = 0; j < 2; j++){
                scanf("%d", &t[i][j]);
            }
        int l = 0, r = 1e7;
        while(l < r){
            int mid = (r-l+1)/2+l;
            build(mid);
            if(solve())
                l = mid;
            else
                r = mid-1;
        }
        printf("%d\n", l);
    }
    return 0;
}
/*
 10
 44 156
 153 182
 48 109
 160 201
 55 186
 54 207
 55 165
 17 58
 132 160
 87 197
*/
View Code

https://vjudge.net/problem/UVALive-3713

技术分享图片
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<cmath>
using namespace std;
const int maxn = 100007;
int age[maxn], n, m, tot, S[maxn*2], scnt;
double ave;
bool mark[maxn*2];
struct Edge{
    int v, nxt;
    Edge(){}
    Edge(int v, int nxt):v(v), nxt(nxt){}
}edge[100007*4];
int head[maxn*2];
void addedge(int u, int v){
    edge[tot] = Edge(v, head[u]);
    head[u] = tot++;
}
void conj(int x, int detx, int y, int dety){
    x = 2*x+detx;
    y = 2*y+dety;
    addedge(x^1, y);
    addedge(y^1, x);
}
bool dfs(int x){
    if(mark[x])
        return true;
    if(mark[x^1])
        return false;
    mark[x] = true;
    S[scnt++] = x;
    for(int i = head[x]; ~i; i = edge[i].nxt){
        int v = edge[i].v;
        if(!dfs(v))
            return false;
    }
    return true;
}
bool solve(){
    memset(mark, false, sizeof mark);
    for(int i = 0; i < 2*n; i+=2){
        if(!mark[i]&&!mark[i+1]){
            scnt = 0;
            if(!dfs(i)){
                for(int j = 0; j < scnt; j++){
                    mark[S[j]] = false;
                }
                scnt = 0;
                if(!dfs(i+1))
                    return false;
            }
        }
    }
    return true;
}
int main(){
    while(scanf("%d%d", &n, &m)){
        tot = 0;
        memset(head, -1, sizeof head);
        if(!n&&!m)
            break;
        double sum = 0;
        for(int i = 0; i < n; i++){
            scanf("%d", &age[i]);
            sum += age[i];
        }
        ave = sum/n;
        while(m--){
            int u, v;
            scanf("%d%d", &u, &v);
            u--;
            v--;
            if((age[u]<ave&&age[v]<ave) || (age[u]>=ave&&age[v]>=ave)){
                conj(u, 0, v, 0);
                conj(u, 1, v, 1);
            }
            else{
                conj(u, 1, v, 1);
            }
        }
        if(!solve()){
            puts("No solution.");
        }
        else{
            for(int i = 0; i < 2*n; i+=2){
                if(mark[i]){
                    printf("C\n");
                }
                else{
                    if(age[i/2]>=ave)
                        printf("A\n");
                    else
                        printf("B\n");
                }
            }
        }
    }
    return 0;
}
/*
 16 20 21
 22
 23
 24
 25
 26
 27
 28 101 102 103 104 105 106 107 108
 1 2 3 4 5 6 7 8
 9 10 11 12 13 14 15 16 1 10 2 9
 3 12 4 11 5 14 6 13 7 16 8 15 1 12 1 13 3 16 6 15 0 0*/
View Code

由于拆点,点数应该是2n...

每个字句a|b 加边!a -> b, !b -> a, 两条边呢...

点和边开的不够大,一言不合就爆掉了...

 

以上是关于2sat的主要内容,如果未能解决你的问题,请参考以下文章

POJ - 3678 - Katu Puzzle(2SAT)

Ex3_28 在2SAT问题中,给定一个字句的集合..._第十二次作业

CF553C Love Triangles(带权并查集)

HDOJ 1814 Peaceful Commission

{TwoSAT}

BZOJ 4945 [Noi2017]游戏