2-SAT 学习笔记 例题:#LOJ2305 NOI2017 游戏

版权声明:_ https://blog.csdn.net/lunch__/article/details/81878969

2 S A T 是一个不是很难的建模的问题(比起网络流)

是由很多个最多只有两个布尔变量限制的东西

求解一般是要找到题目满足限制的可行解

这里大概写下算法流程,就不写证明了

首先对题目限制进行连边建图,边的意思表示起点的信息能推出终点的信息

建好图后如果一个变量为真和一个变量为假在一个强连通分量里,那么肯定是无解的

所以我们用 T a r j a n 缩点

求解方案的时候建反图拓扑排序就好了 即选择原图拓扑序大的

但是 T a r j a n 有个很棒的性质 就是它的强连通分量编号大小与拓扑序是相反的

利用这个性质我们就不用拓扑排序了 直接比较强连通分量编号大小,优先选择小的

洛谷模板
这里面是把取 x 这个变量记为 x + n
所以输出的时候是 b e [ i + n ] < b e [ i ]
直接建图缩点就好了

#include<bits/stdc++.h>

using namespace std;

const int N = 2e6 + 10;
int head[N], nxt[N], to[N], e; 
int n, m, color, be[N];

void add(int x, int y) {
    to[++ e] = y; nxt[e] = head[x]; head[x] = e;
}

namespace Tarjan {
    int low[N], dfn[N], cnt, Sta[N], top, ins[N];
    void dfs(int x) {
        dfn[x] = low[x] = ++ cnt; Sta[++ top] = x; ins[x] = 1;
        for(int i = head[x], v = to[i]; i; i = nxt[i], v = to[i]) {
            if(!dfn[v]) {
                dfs(v);
                low[x] = min(low[x], low[v]);
            }
            else if(ins[v])
                low[x] = min(low[x], dfn[v]);
        }
        if(low[x] == dfn[x]) {
            ++ color;
            do {
                ins[Sta[top]] = 0;
                be[Sta[top]] = color;
            }while(Sta[top --] != x);
        }
    }
}

bool Topsort() {
    queue<int> q;
    for(int i = 1; i <= n; ++ i)
        if(be[i] == be[i + n]) 
            return false;
    puts("POSSIBLE");
    for(int i = 1; i <= n; ++ i)
        printf("%d ", be[i + n] < be[i]);
    return true;
}

int main() {
#ifndef ONLINE_JUDGE
    freopen("4782.in", "r", stdin);
    freopen("4782.out", "w", stdout);
#endif
    int a1, x, a2, y;
    scanf("%d%d", &n, &m);
    for(int i = 1; i <= m; ++ i) {
        scanf("%d%d%d%d", &a1, &x, &a2, &y);
        add(a1 + (x ^ 1) * n, a2 + y * n);
        add(a2 + (y ^ 1) * n, a1 + x * n);
    }
    for(int i = 1; i <= (n << 1); ++ i)
        if(!Tarjan::dfn[i])
            Tarjan::dfs(i);
    if(!Topsort())
        puts("IMPOSSIBLE");
    return 0;
}

2-SAT模板链接
这个差不多算 2 s a t 模板吧…但我还是调了蛮久的
就发现 x 类型的地图不好处理
但是发现 x 地图数量很少
考虑枚举每一种 x 的情况

对于每种情况跑一次 2 s a t 的判定是否有解就可以了

当然 一个比较恶心的地方就是连边的时候连出了不存在的点

如果不存在的点是起点还好,怎么都不会被选到

但是如果不存在的点是终点,我们显然就不能让起点被选

那我们就直接向这个点所对应的另一个限制点连边

以上感谢 y l s o i   j u l a o 的讲解

还有一个东西就是拓扑序的性质,打完之后发现怎么和之前打的板子不一样

这个题如果 i 拓扑序在 i 前面,意味着选了 i 就要选 i
所以我们肯定选 i ,即拓扑序大的点.

复杂度 O ( 2 d ( n + m ) )

Codes

#include<bits/stdc++.h>

#define pb push_back
#define mem(a, b) memset(a, b, sizeof(a))

using namespace std;

const int N = 1e5 + 10;
vector<int> G[N];
int X[10], n, d, m;
int be[N], color;
string s;

struct edge {
    char hx, hy;
    int x, y;
}E[N];

int num(int _, char __) {
    if(s[_ - 1] == 'a') return __ == 'C';
    if(s[_ - 1] == 'b') return __ == 'C';
    if(s[_ - 1] == 'c') return __ == 'B'; 
}

char cha(int _, int __) {
    //cout << s[_ - 1] << endl;
    if(s[_ - 1] == 'a') return 'C' - __;
    if(s[_ - 1] == 'b') return 'C' - (__ << 1);
    if(s[_ - 1] == 'c') return 'B' - __;
}

namespace Tarjan {
    int dfn[N], low[N], Sta[N], top, cnt, ins[N];

    void Graph_Clear() {
        vector<int> Ga[N]; swap(Ga, G);
        cnt = top = color = 0;
        mem(dfn, 0), mem(be, 0);
    }

    void dfs(int x) {
        low[x] = dfn[x] = ++ cnt; Sta[++ top] = x; ins[x] = 1;
        for(auto v : G[x]) {
            if(!dfn[v]) {
                dfs(v);
                low[x] = min(low[x], low[v]);
            }
            else if(ins[v])
                low[x] = min(low[x], dfn[v]);
        }
        if(low[x] == dfn[x]) {
            ++ color;
            do {
                ins[Sta[top]] = 0;
                be[Sta[top]] = color;
            }while(Sta[top --] != x);
        }
    }
}
int main() {
#ifndef ONLINE_JUDGE
    freopen("2305.in", "r", stdin);
    freopen("2305.out", "w", stdout);
#endif
    cin >> n >> d >> s;
    for(int i = 0; i < n; ++ i)
        if(s[i] == 'x') 
            X[++ X[0]] = i;
    scanf("%d", &m);
    for(int i = 1; i <= m; ++ i) {
        scanf("%d", &E[i].x); cin >> E[i].hx;
        scanf("%d", &E[i].y); cin >> E[i].hy;
    }
    for(int j = 0; j < (1 << d); ++ j) {
        int flag = 0;
        Tarjan::Graph_Clear();
        for(int i = 1; i <= X[0]; ++ i) s[X[i]] = (1 << (i - 1)) & j ? 'a' : 'b';
        for(int i = 1; i <= m; ++ i) {
            if(E[i].hx + 32 == s[E[i].x - 1]) continue;
            int fx = num(E[i].x, E[i].hx), fy = num(E[i].y, E[i].hy);
            if(E[i].hy + 32 == s[E[i].y - 1]) {
            //  cout << i << ' ' << E[i].x + fx * n << ' ' << E[i].x + (fx ^ 1) * n << endl;
                G[E[i].x + fx * n].pb(E[i].x + (fx ^ 1) * n); continue;
            }
            //cout << fx << ' ' << E[i].hx << ' ' << fy << ' ' << E[i].hy << endl;
            G[E[i].x + fx * n].pb(E[i].y + fy * n), G[E[i].y + (fy ^ 1) * n].pb(E[i].x + (fx ^ 1) * n);
        //  cout << i << ' ' << E[i].x + fx * n << ' ' << E[i].y + fy * n << endl;
        //  cout << i << ' ' << E[i].y + (fy ^ 1) * n << ' ' << E[i].x + (fx ^ 1) * n << endl;
        }
        //puts("");
        for(int i = 1; i <= (n << 1); ++ i) if(!Tarjan::dfn[i]) Tarjan::dfs(i);
        for(int i = 1; i <= n; ++ i) if(be[i] == be[i + n]) {flag = 1; break;}
        if(flag) continue;
        for(int i = 1; i <= n; ++ i) 
            printf("%c", cha(i, be[i] < be[i + n]));
        return 0;
    }
    puts("-1");
    return 0;
}

猜你喜欢

转载自blog.csdn.net/lunch__/article/details/81878969