码迷,mamicode.com
首页 > 其他好文 > 详细

P4782 【模板】2-SAT 问题

时间:2019-03-27 21:15:11      阅读:160      评论:0      收藏:0      [点我收藏+]

标签:imp   ssi   top   拓扑序   避免   思路   输出   scanf   时间   

芝士

2-SAT问题就是一种给出n个变量,满足一些二元限制比如(x取1,y必须取0),要求求出n个变量赋值的合法方案的题目
3-SAT及更多是NP完全问题
2-SAT求解可以用tarjan,时间复杂度\(O(n+m)\),但是要求输出字典序最小解的时候只有\(O(nm)\)的算法
算法流程就是要拆点连边,x表示x取1,x+n表示x取0,然后用边表示“必须”的条件,<x,y+n>表示x取1,y必须取0,则如果选了x,相连的所有点都要选
所以处于同一强连通分量里的点必须都选,无解显然是x与x+n在一个强连通分量中
输出方案的时候,为了避免冲突和保证能构造出解,需要在缩点之后的DAG里找到拓扑序的反序小的点先选(就是对后面没有影响的,因为选了x之后后面的点都要选),tarjan之后的sccno正好是反向的拓扑序,所以优先选标号小的条件即可

思路

板子,转化成x为a^1时y必须为b、y为b^1时x必须为a即可

代码

#include <cstdio>
#include <cstring>
#include <algorithm>
#include <stack>
using namespace std;
int u[2000100],v[2000100],fir[2000100],nxt[2000100],cnt,low[2000100],dfn[2000100],dfs_clock,vis[2000100],scc_cnt,sccno[2000100],n,m,choose[2000100];
stack<int> S;
void addedge(int ui,int vi){
    ++cnt;
    u[cnt]=ui;
    v[cnt]=vi;
    nxt[cnt]=fir[ui];
    fir[ui]=cnt;
}
void tarjan(int u){
    low[u]=dfn[u]=++dfs_clock;
    S.push(u);
    vis[u]=true;
    for(int i=fir[u];i;i=nxt[i]){
        if(!dfn[v[i]]){
            tarjan(v[i]);
            low[u]=min(low[u],low[v[i]]);
        }
        else if(vis[v[i]])
            low[u]=min(low[u],low[v[i]]);
    }
    if(low[u]==dfn[u]){
        scc_cnt++;
        while(1){
            int x=S.top();
            S.pop();
            vis[x]=false;
            sccno[x]=scc_cnt;
            if(x==u)
                break;
        }
    }
}
void get_scc(void){
    for(int i=1;i<=2*n;i++)
        if(!dfn[i])
            tarjan(i);
}
int main(){
    scanf("%d %d",&n,&m);
    for(int i=1;i<=m;i++){
        int xi,xj,a,b;
        scanf("%d %d %d %d",&xi,&a,&xj,&b);
        addedge(xi+(a^1)*n,xj+b*n);
        addedge(xj+(b^1)*n,xi+a*n);
    }
    get_scc();
    bool f=true;
    for(int i=1;i<=n;i++){
        if(sccno[i]==sccno[i+n]){
            f=false;
            break;
        }
        if(sccno[i]<sccno[i+n])
            choose[i]=0;
        else
            choose[i]=1;
    }
    if(!f){
        printf("IMPOSSIBLE\n");
    }
    else{
        printf("POSSIBLE\n");
        for(int i=1;i<=n;i++)
            printf("%d ",choose[i]);
        printf("\n");
    }
    return 0;
}

P4782 【模板】2-SAT 问题

标签:imp   ssi   top   拓扑序   避免   思路   输出   scanf   时间   

原文地址:https://www.cnblogs.com/dreagonm/p/10610525.html

(0)
(0)
   
举报
评论 一句话评论(0
登录后才能评论!
© 2014 mamicode.com 版权所有  联系我们:gaon5@hotmail.com
迷上了代码!