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;
    }
    
    越自律,越自由
  • 相关阅读:
    Python趣味小问题——用积分思想计算圆周率
    通过自动化测试发现缺陷
    Xcodebuild稳定性测试go脚本
    Google软件测试之道笔记与总结
    PHP打印日志类
    Python3用多线程替代for循环提升程序运行速度
    卡死进程检杀工具
    ECharts饼图自定义
    解决bootstrap-table表头filter-control select控件被遮挡显示不全的问题
    Html表格和表头文字不换行
  • 原文地址:https://www.cnblogs.com/ha-chuochuo/p/14483492.html
Copyright © 2011-2022 走看看