2-SAT(小结)

学习目标:

2-SAT(小结)
这个人讲的很好,我就不详细讲了

学习内容:

注意点

1.适用范围:x1&x2=0,问给出 m 个关系表达式后,能否给这 n 个变量找出一个赋值的方法,使得满足所有的表达式;

2.对于每个变量都只有两种取值:0 / 1,那么对于每个点,我们把每个变量拆成 true 点和 false 点;

3.要注意只有关系明确的时候才能建边; 其中一个不成立则另一个一定成立(这是明确的关系);

4.无解的情况:某一个变量的 false 能走到 true,从 true 也能走到 false,也就是说,某一个变量的两个取值在同一个强连通分量内的话,就说明无解。

5.输出解集的时候,我们要注意,tarjan给连通分量标号的拓扑序是反序,拓扑序越大,扑序越大的点其所在强联通分量编号越小,我们选择编号小的就行了。

6.至于输出字典序最小的解集,就要O(n*m)爆搜了。


学习例题:

1900
2000
1900
字典序最小解

学习产出:

感觉都挺水的,不知道是题目水还是数据水,放俩板子搁这吧。

拓扑序任意解:O(n+m)

扫描二维码关注公众号,回复: 12410525 查看本文章
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int N=2e6+6;
const int mod=1e9+7;
vector<int>G[N];
stack<int>s;
int n,m,x,y,a,b,tot,colnum;
int dfn[N],col[N],vis[N],low[N];
void tarjan(int u) {
    
    
    dfn[u]=low[u]=++tot;
    vis[u]=1,s.push(u);
    for(auto v:G[u]) {
    
    
        if(!dfn[v])
            tarjan(v),low[u]=min(low[u],low[v]);
        else if(vis[v])
            low[u]=min(low[u],dfn[v]);
    }
    if(low[u]==dfn[u]) {
    
    
        ++colnum;
        while(true) {
    
    
            int v=s.top();
            s.pop();
            vis[v]=0,col[v]=colnum;
            if(u==v)break;
        }
    }
}
void add(int u,int v) {
    
    
    G[u].push_back(v);
}
int main() {
    
    
    scanf("%d%d",&n,&m);
    for(int i=1; i<=m; i++) {
    
    
        scanf("%d%d%d%d",&a,&x,&b,&y);
        if(x==0&&y==0) {
    
    
            add(a+n,b);
            add(b+n,a);
        } else if(x==0&&y==1) {
    
    
            add(b,a);
            add(a+n,b+n);
        } else if(x==1&&y==0) {
    
    
            add(b+n,a+n);
            add(a,b);
        } else if(x==1&&y==1) {
    
    
            add(b,a+n);
            add(a,b+n);
        }
    }
    for(int i=1; i<=n*2; i++) {
    
    
        if(!dfn[i])tarjan(i);
    }
    for(int i=1; i<=n; i++) {
    
    
        if(col[i]==col[i+n]) {
    
    
            printf("IMPOSSIBLE\n");
            return 0;
        }
    }
    printf("POSSIBLE\n");
    for(int i=1; i<=n; i++) {
    
    
        if(col[i]<col[i+n])printf("0 ");
        else printf("1 ");
    }
    printf("\n");
}

字典序最小解:O(n*m)复杂度有些玄学

#include<bits/stdc++.h>
using namespace std;
const int maxn = 8006;
int n, m, stak[maxn], top, col[maxn<<1];
vector<int> G[maxn<<1];
int mir(int x) {
    
    
    return x&1? x+1: x-1;
}
bool paint(int x) {
    
    
    if(col[x])
        return col[x] == 1;
    col[x] = 1;
    col[mir(x)] = 2;
    stak[++top] = x;
    for(unsigned int i = 0; i < G[x].size(); ++i) {
    
    
        int id = G[x][i];
        if(!paint(id))
            return 0;
    }
    return 1;
}

bool work() {
    
    
    for(int i = 1; i <= (n<<1); ++i)
        if(!col[i]) {
    
    
            top = 0;
            if(!paint(i)) {
    
    
                while(top)  col[stak[top]] = col[mir(stak[top])] = 0, top--;
                if(!paint(mir(i)))
                    return 0;
            }
        }
    return 1;
}

int main() {
    
    
    while(scanf("%d%d", &n, &m) != EOF) {
    
    
        for(int i = 1; i <= m; ++i) {
    
    
            int x, y;
            scanf("%d%d", &x, &y);
            G[x].push_back(mir(y));
            G[y].push_back(mir(x));
        }
        if(work()) {
    
    
            for(int i = 1; i <= (n<<1); ++i)
                if(col[i] == 1)
                    printf("%d\n", i);
        } else
            printf("NIE\n");
        for(int i = 1; i <= (n<<1); ++i)
            G[i].clear(), col[i] = 0;
    }
}

猜你喜欢

转载自blog.csdn.net/qq_43653111/article/details/108364503