poj 3207 2-SAT问题

思路:将线段按开始点的升序排序,对线段尾节点进行判断,若存在交叉,那么这两条线段就不能同时在内或同时在外。这样将每条线段在内和在外看成两个状态ii',i表示线段在内,i'表示线段在外。假使线段i和线段j相交,那么ij是矛盾,且i'j'是矛盾。

具体见代码

#include<iostream>

#include<cstdio>

#include<algorithm>

#include<cstring>

#include<queue>

#define Maxn 2010

#define Maxm Maxn*Maxn

using namespace std;

int vi[Maxn],head[Maxn],dfn[Maxn],low[Maxn],e,n,lab,top,num,m,id[Maxn],Stack[Maxn];

struct Edge{

    int u,v,next;

}edge[Maxm];

struct line{

    int u,v;

}p[Maxn];

void init()//初始化

{

    memset(vi,0,sizeof(vi));

    memset(head,-1,sizeof(head));

    memset(dfn,0,sizeof(dfn));

    memset(low,0,sizeof(low));

    memset(id,0,sizeof(id));

    e=lab=top=num=0;

}

void add(int u,int v)//加边

{

    edge[e].u=u,edge[e].v=v,edge[e].next=head[u],head[u]=e++;

}

int cmp(line a,line b)

{

    if(a.u==b.u)

        return a.v<b.v;

    return a.u<b.u;

}

void Tarjan(int u)//找出强连通分支

{

    int i,j,v;

    //cout<<u<<endl;

    dfn[u]=low[u]=++lab;

    Stack[top++]=u;

    vi[u]=1;

    for(i=head[u];i!=-1;i=edge[i].next)

    {

        v=edge[i].v;

        if(!dfn[v])

        {

            Tarjan(v);

            low[u]=min(low[u],low[v]);

        }

        if(vi[v])

        low[u]=min(low[u],dfn[v]);



    }

    if(low[u]==dfn[u])

    {

        ++num;

        do{

            i=Stack[--top];

            vi[i]=0;

            id[i]=num;

        }while(i!=u);

    }

}

int solve()

{

    int i,j;

    for(i=1;i<=m*2;i++)

        if(!dfn[i])

        Tarjan(i);

    for(i=1;i<=m;i++)

        if(id[i]==id[i+m])

            return 0;//有矛盾则结束

    return 1;

}

int cross(line a,line b)

{

    if(a.v>b.u&&a.v<b.v)

        return 1;

    return 0;

}

int main()

{

    int i,j,a,b;

    while(scanf("%d%d",&n,&m)!=EOF)

    {

        init();

        for(i=1;i<=m;i++)

        {

            scanf("%d%d",&a,&b);

            p[i].u=a<b?a:b;

            p[i].v=a>b?a:b;

        }

        sort(p+1,p+1+m,cmp);

        for(i=1;i<m;i++)

        for(j=i+1;j<=m;j++)

        {

            if(cross(p[i],p[j]))//如果有两条线的区间相交,那么存在矛盾

            {

                add(i,j+m);

                add(j,i+m);

                add(i+m,j);

                add(j+m,i);

            }

        }

        if(solve())

            printf("panda is telling the truth...\n");

        else

            printf("the evil panda is lying again\n");

    }

    return 0;

}

 

你可能感兴趣的:(poj)