【POJ3207】Ikki's Story IV - Panda's Trick- 2-SAT

测试地址:Ikki’s Story IV - Panda’s Trick
题目大意:n个点排成一个环形,现在要连m条边,一条边要么连在环形的里面,要么连在环形的外面,问有没有一种连法使得这些边两两之间不相交(在点上相交不算)?
做法:我的博客已经持续更新一周年了,可喜可贺可喜可贺……
至于为什么这一个月没有更新呢……说来话长,你们只需要知道我NOI2017考崩了就好(T_T)。
这一题是一个2-SAT的模型。
什么叫2-SAT?2-SAT是2-适定性问题的简称,差不多是这样一个模型:给若干个东西,这些东西两两之间可能有限制条件,问有没有满足所有条件的选法。之所以命名为2-SAT就是因为这些限制条件是两两之间的条件,而k-SAT问题在k>2时已经被证明是NPC问题,只有2-SAT有比较优美的解法。
至于解2-SAT,网上有很多教程,这里就不细讲了,大概就是将每个东西分成两点,分别表示取或不取这个东西(以下称这两个点为A和非A),那么问题就转化为在每组两个点里取且仅取一个点,问有没有满足限制条件的取法。我们可以把两两之间的限制条件变成有向边的形式,通常有向边A->B表示要取A必须也取B,经过一些转化后就可以求解,主要是用强连通分量,这里就不再赘述了。
那么这一题的模型要转化为2-SAT十分简单,每条边有连里边和连外边两种选择,且只能选择一种,这已经提示了这题是2-SAT。再看限制条件,两条边不能在点外相交。经过探究,两条边在点外相交的充要条件是:两条边没有公共顶点并连在同一侧,且其中一条边的两个端点分别处在另一条边将环分成的两个部分。于是我们判断两条边是不是满足了“没有公共顶点”和最后那个条件(太长懒得写),如果同时满足,那么就说明它们的连法受到限制:不能连在环形的同侧,也就是说,对于冲突的两条边A,B,我们不能同时选择点A和B,也不能同时选点非A和非B。那么应该怎么连边呢?很简单,点A,B不能同时被选,那么就表示选了A就必须选非B,选了B就必须选非A,按照这样的转化连边即可。建完图之后,求图的强连通分量,如果在同一组的两个点处在同一个强连通分量,那么问题无解,否则问题肯定有解。其实可以求出任意一个解,但这题只需要判断有没有解,所以就下次再说啦~
总的时间复杂度:O(m2),可以通过本题(0ms)。
以下是本人代码:

#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <iostream>
#include <algorithm>
using namespace std;
int n,m,a[510],b[510],tot=0,tim=0,tott,first[2010]={0};
struct {int v,next;} e[2000010];
int dfn[1010],low[1010],belong[1010]={0},stack[1010]={0},top=0;
int in[2010]={0};
bool vis[1010]={0};

void insert(int a,int b)
{
    e[++tot].v=b;
    e[tot].next=first[a];
    first[a]=tot;
}

void dfs(int v)
{
    vis[v]=1;
    dfn[v]=++tim;
    low[v]=dfn[v];
    stack[++top]=v;
    int now=top;
    for(int i=first[v];i;i=e[i].next)
    {
        if (!vis[e[i].v])
        {
            dfs(e[i].v);
            low[v]=min(low[v],low[e[i].v]);
        }
        else if (!belong[e[i].v]) low[v]=min(low[v],dfn[e[i].v]);
    }
    if (low[v]==dfn[v])
    {
        tott++;
        for(int i=now;i<=top;i++)
            belong[stack[i]]=tott;
        top=now-1;
    }
}

void tarjan()
{
    for(int i=0;i<2*m;i++)
        if (!vis[i]) dfs(i);
    tott++;
    for(int i=1;i<=top;i++)
        belong[stack[i]]=tott;
}

int main()
{
    scanf("%d%d",&n,&m);
    for(int i=0;i<m;i++)
    {
        scanf("%d%d",&a[i],&b[i]);
        if (a[i]>b[i]) swap(a[i],b[i]);
        for(int j=0;j<i;j++)
        {
            if (a[i]==a[j]||b[i]==a[j]||a[i]==b[j]||b[i]==b[j]) continue;
            bool f1=(a[j]>a[i]&&a[j]<b[i]),f2=(b[j]>a[i]&&b[j]<b[i]);
            if (f1!=f2)
            {
                insert(2*j,2*i+1);
                insert(2*j+1,2*i);
                insert(2*i,2*j+1);
                insert(2*i+1,2*j);
            }
        }
    }

    tott=2*m-1;
    tarjan();

    for(int i=0;i<m;i++)
        if (belong[2*i]==belong[2*i+1])
        {
            printf("the evil panda is lying again");
            return 0;
        }
    printf("panda is telling the truth...");

    return 0;
}
posted @ 2017-08-01 18:13  Maxwei_wzj  阅读(72)  评论(0编辑  收藏  举报