2-SAT学习记录

2-SAT学习记录,学习动力源于之前学完强连通去做教练挂的联通图的题目,第一题看了半天不会做,最后发现是2-SAT算法= = ,不会,遂学习之。

关于2-SAT算法有一篇很好的学习博客 2-SAT研究笔记

2-SAT算法感觉是一个很经典的算法,上面的笔记更是对算法的每一部分进行了推导和证明。其实看懂的话应该难度不是态度,需要前置技能 强连通分量 拓扑排序,剩下的就是一种思想,在此贴一下经典的2-SAT模型,转载自以上的2-SAT学习笔记。

模型一:两者(A,B)不能同时取

  那么选择了A就只能选择B’,选择了B就只能选择A’
  连边A→B’,B→A’
 

模型二:两者(A,B)不能同时不取

  那么选择了A’就只能选择B,选择了B’就只能选择A
  连边A’→B,B’→A
 

模型三:两者(A,B)要么都取,要么都不取

  那么选择了A,就只能选择B,选择了B就只能选择A,选择了A’就只能选择B’,选择了B’就只能选择A’
  连边A→B,B→A,A’→B’,B’→A’
 

模型四:两者(A,A’)必取A

  那么,那么,该怎么说呢?先说连边吧。
  连边A’→A
  你想出来为什么了吗?也许你在想,哇塞,这样就保证了在反图中A在拓扑序中靠前,然后就会先选择A,呵呵,你错了。
  虽然,我在一些人的博客中见到了这样的解释,但是,我还是非常负责任的告诉你,这是不全面的。
  我们从A’向A连边,要的不仅是拓扑序,还有判可行与否。在2-sat图当中,若该图是可行的,就意味着如果从A到A’有路径的话,A’到A是没有路径的,因为如果有路径,它们就成一个块了,不会被判为可行,那么,如果A到A’是有路径的话,在反图中,A’就到A有路径,那么拓扑里,A’就会因为靠前被标记为“选择”,并未满足条件。并且,我们应当确信的是,解的多情况依赖的是拓扑排序的多情况,不按拓扑序来的话,解就是错误的,或者说是不成立的,也就是说,我们拓扑序先选择A的话,就会导致别的约束条件的不成立,那么在我们引入了A’到A的这条边后,A就与A’在同一块中了,算法会报告不可行信息。若是原图本来就满足A到A’有路径,然而A’到A无路径的话,我们添一条边,只是确定了其有解,但丝毫不会影响到拓扑排序,只有当A到A’之间根本不存在路径的时候,才会影响到其拓扑排序,所以,真相是这样的。
  是不是有人已经开始疑惑,讲了这么久的对称性,整个算法都是依赖对称性才得以成立的,那么怎么一下引入一条边让图不对称了算法还成立呢!关于这个问题,其实很好解释,仔细想想,对称性确保的是什么?它确保的可是原图有解,则一定可以构造出来。现在我们引入了一条边A’→A,若通过上述判断,让其无解了,那么对后面显然是没有影响的,毕竟算法都没执行下去了,还算什么影响。如果还有解呢?这说明了什么?这说明了A’到A原本就存在一条路径,或者A’到A之间根本就没有路径。如果有路径的话,那么我多增加一条有区别吗?它会影响到算法的任何一步吗?显然不会啊,那如果原本没有路径的话呢?没有路径意味着谁在拓扑序列中靠前都是可能的,在有了该边后(反边为A→A’),A将在拓扑序列中靠前,被标记为“选择”,那么我们会对A’进行直接标记,此时,A’到A是没有路可走的,它根本就无法访问到A,所以在标记的这个过程中,这条路径根本就没有影响到图的任何对称性,在拓扑排序后,你完全可以当其不存在。

  模型其实都大同小异,还是看自己去变幻,比如模型一和模型二其实就是一样的,只是针对的点不同而已。一般,如果2-sat感觉很明显的话,就用2-sat做好了,不过,千万记得考虑好如何降低构图的复杂度噢!复杂度如果太高,就还是再另辟蹊径吧,也许有更加好的方法嗯。

2-SAT学习持续进行中......

POJ-3207:Ikki's Story IV - Panda's Trick

题目大意:

其实这道题刚开始愣是没看懂题目什么意思,主要是没想清楚如何跟2-SAT联合起来。图论学的太少导致对建图这方面根本不敏感。。。

其实给了你m对边连接的方案时,我们会发现,如果两个边会相交,那么首先要满足4个点刚好交叉的情况,其次还要这两条边同时连在圆内和圆外。

解题思路:

那么就可以依据此建图,对于每一条边分为圆内圆外,那么对于2-SAT模式对于有矛盾的边,第i条在圆内 第j条在圆外,同里第i条在圆外 第j条就必须在圆内。

这道题不需要输出方案,所以只需判断是否可行即可,判断方法在上面笔记中提到过。

Ac代码:

#include<iostream>
#include<cstdio>
#include<cstdlib>
#include<algorithm>
#include<cmath>
#include<cstring>
#include<stack>
#include<vector>
#define lson rt<<1
#define rson rt<<1|1
using namespace std;
typedef long long ll;
const int maxn=4e3+5;
const int INF=1e9+7;
const ll mod=998244353;
int n,m,a[maxn],b[maxn];

int tot,cnt,dfn[maxn],low[maxn],belong[maxn];
vector<int> v[maxn];
bool vis[maxn];
stack<int> s;

void tarjan(int u)
{
    low[u]=dfn[u]=++tot;
    s.push(u),vis[u]=1;
    for(int i=0;i<v[u].size();i++)
    {
        int g=v[u][i];
        if(!dfn[g])
        {
            tarjan(g);
            low[u]=min(low[u],low[g]);
        }
        else if(vis[g]) low[u]=min(low[u],dfn[g]);
    }
    if(dfn[u]==low[u])
    {
        cnt++;
        while(!s.empty())
        {
            int now=s.top();
            s.pop();vis[now]=0;
            belong[now]=cnt;
            if(now==u) break;
        }
    }
}

void add(int x,int y)
{
    v[x].push_back(y);
}

void build()    //建图
{
    for(int i=1;i<=m;i++)
    {
        for(int j=i+1;j<=m;j++)
        {
            if((a[i]<a[j]&&a[j]<b[i]&&b[i]<b[j])||(a[j]<a[i]&&a[i]<b[j]&&b[j]<b[i]))
            {
                add(i*2-1,j*2);
                add(i*2,j*2-1);
                add(j*2,i*2-1);
                add(j*2-1,i*2);
            }
        }
    }
}
bool check()    //判断2-SAT的合法性
{
    for(int i=1;i<=m;i++)
        if(belong[i*2-1]==belong[i*2]) return 0;
    return 1;
}
int main()
{
    scanf("%d%d",&n,&m);
    for(int i=1;i<=m;i++)
    {
        scanf("%d%d",&a[i],&b[i]);
        if(a[i]>b[i]) swap(a[i],b[i]);
    }
    build();
    for(int i=1;i<=2*m;i++)
        if(!dfn[i]) tarjan(i);
    if(check()) printf("panda is telling the truth...\n");
    else printf("the evil panda is lying again\n");
    //system("pause");
}

猜你喜欢

转载自blog.csdn.net/f2935552941/article/details/81071600