zoukankan      html  css  js  c++  java
  • 2-SAT算法+方案输出

    定义

    给出 (n) 个集合,每个集合都有两个元素,已知若干对 (<a,b>)((a,b)属于不同的集合)不能同时出现,从每个合集都选一个元素,判断能否在 (n) 个集合里选出 (n) 个元素。


    解决方法:

    • 建有向图。对于某一个条件如 (avee b) 为真,我们可以增加两条边 (!a ightarrow b) , (!b ightarrow a)
    • 假设有 (a1,a2)(b1,b2) 两对,已知 (a1)(b2) 间有矛盾,于是为了方案自洽,由于两者中必须选一个,所以我们就要拉两条条有向边 (a1,b1)(b2,a2) 表示选了 (a1) 则必须选 (b1),选了 (b2) 则必须选 (a2) 才能够自洽。
    • 在建完图以后,我们就可以通过 (tarjan) 算法求强连通分量((scc))。
    • 如果某一个 (scc) 内出现相同集合中的元素, 那么答案就是 (impossible)
    • 对于其他情况就是有方案的。
    • 对于一个集合中该选哪个,通过对于其属于 (scc) 的编号决定,每次都选 (scc) 编号更小的即可。

    代码:

    以louguP4782为例

    #include<bits/stdc++.h>
    #define MAXN 4000400
    using namespace std;
    stack<int> stk;
    // instk:是否在栈中  scc:每个点所属的强连通分量编号  cscc:强连通分量的数量
    int dfsn[MAXN], low[MAXN], instk[MAXN], scc[MAXN], cnt, cscc;
    int ans[MAXN];  //记录答案
    vector<int>edges[MAXN];
    void tarjan(int p)
    {
        low[p] = dfsn[p] = ++cnt;
        instk[p] = 1;
        stk.push(p); // 进栈
        for (auto q : edges[p])
        {
            if (!dfsn[q]) // 未访问过
            {
                tarjan(q); // 递归地搜索
                low[p] = min(low[p], low[q]);
            }
            else if (instk[q]) // 访问过,且q可达p
                low[p] = min(low[p], dfsn[q]);
        }
        if (low[p] == dfsn[p]) // 发现强连通分量的根
        {
            int top;
            cscc++;
            do
            {
                top = stk.top();
                stk.pop();
                instk[top] = 0;
                scc[top] = cscc; // 记录所属的强连通分量
            } while (top != p); // 直到弹出p才停止
        }
    }
    
    int main()
    {
        int n,m;
        scanf("%d %d",&n,&m);
        for(int i=1;i<=m;i++){
            int x,a,y,b;
            scanf("%d%d%d%d",&x,&a,&y,&b);
            int xx1,yy1,xx2,yy2;
            if(a==0){
                xx1=2*x;
                xx2=2*x-1;
            }else{
                xx1=2*x-1;
                xx2=2*x;
            }
            if(b==0){
                yy1=2*y-1;
                yy2=2*y;
            }else{
                yy1=2*y;
                yy2=2*y-1;
            }
            edges[xx1].push_back(yy1);
            edges[yy2].push_back(xx2);
        }
        for(int i=1;i<=2*n;i++){
            if(!dfsn[i]){
                tarjan(i);
            }
        }
        for(int i=1;i<=n;i++){
            if(scc[2*i-1]==scc[2*i]){
                cout<<"IMPOSSIBLE"<<endl;
                return 0;
            }else{
                if(scc[2*i-1]<scc[2*i]){   //选择tarjan后,所属连通分量编号小的。
                    ans[i]=0;
                }else{
                    ans[i]=1;
                }
            }
        }
        cout<<"POSSIBLE"<<endl;
        for(int i=1;i<=n;i++){
            printf("%d%c",ans[i]," 
    "[i==n]);
        }
        return 0;
    }
    
    越自律,越自由
  • 相关阅读:
    在Ubuntu中通过update-alternatives切换软件版本
    SCons: 替代 make 和 makefile 及 javac 的极好用的c、c++、java 构建工具
    mongodb 的使用
    利用grub从ubuntu找回windows启动项
    How to Repair GRUB2 When Ubuntu Won’t Boot
    Redis vs Mongo vs mysql
    java script 的工具
    python 的弹框
    how to use greendao in android studio
    python yield的终极解释
  • 原文地址:https://www.cnblogs.com/ha-chuochuo/p/14483492.html
Copyright © 2011-2022 走看看