POJ 3207 Ikki's Story IV - Panda's Trick(2-SAT)

POJ 3207 Ikki's Story IV - Panda's Trick(2-SAT)

http://poj.org/problem?id=3207

题意:

        平面上有一个圆,圆上有n个点(分别编号0-n-1,按顺序在圆上排列),现在要对着n个点连接m条线,这m条线的两个端点已经给出了(但是没说这些先是连内圈还是连外圈),这个线可以从圆内连或从圆外连.且任意一个点最多只作为一条线的端点.要求任意两条线不相交,问你是否可能?

分析:

        由于题目中需要连接的边都确定给出了,我们可以根据任意两线i与j的端点推断出当i与j同时在圆内或圆外是否一定会相交。

        如果边i与j同在圆的一侧必定相交,那么边i与边j必然一个在圆内,一个在圆外,所以有如下结论:

        i在圆内或j在圆内 且 i在圆外或j在圆外 (想想为什么)

        那么如何判断边i(ui,vi) 与 j(uj,vj) 在圆同一侧时一定会相交呢?

        由于这里端点是在圆上的,所以如果j边的两点中,一点处于i边两端点的ui顺时针走向vi的圆弧内,一点处于i边两端点的vi顺时针走向ui的圆弧内,那么i与j在圆同一侧时一定相交。否则一定不想交(自己画图验证一下)

AC代码:

#include<cstdio>
#include<cstring>
#include<vector>
#include<algorithm>
using namespace std;
const int maxn=1000+10;
const int maxm=500+10;
struct Edge
{
    int u,v;
    Edge(){}
    Edge(int u,int v):u(u),v(v){}
}edges[maxm];
int cnt;
struct TwoSAT
{
    int n;
    vector<int> G[maxm*2];
    int S[maxm*2],c;
    bool mark[maxm*2];

    bool dfs(int x)
    {
        if(mark[x]) return true;
        if(mark[x^1]) return false;
        mark[x]=true;
        S[c++]=x;
        for(int i=0;i<G[x].size();i++)
            if(!dfs(G[x][i])) return false;
        return true;
    }

    void init(int n)
    {
        this->n=n;
        for(int i=0;i<n*2;i++) G[i].clear();
        memset(mark,0,sizeof(mark));
    }

    void add_clause(int x,int xval,int y,int yval)
    {
        x=x*2+xval;
        y=y*2+yval;
        G[x^1].push_back(y);
        G[y^1].push_back(x);
    }

    bool solve()
    {
        for(int i=0;i<2*n;i+=2)if(!mark[i] && !mark[i+1])
        {
            c=0;
            if(!dfs(i))
            {
                while(c>0) mark[S[--c]]=false;
                if(!dfs(i+1)) return false;
            }
        }
        return true;
    }
}TS;
int main()
{
    int n,m;
    scanf("%d%d",&n,&m);
    cnt=0;
    for(int i=0;i<m;i++)
    {
        int u,v;
        scanf("%d%d",&u,&v);
        if(u>v) swap(u,v);
        edges[cnt++]=Edge(u,v);
    }
    TS.init(m);
    for(int i=0;i<m;i++)
        for(int j=i+1;j<m;j++)
            if( (edges[i].u<=edges[j].u&&edges[i].v>=edges[j].u&&edges[i].v<=edges[j].v) ||
                (edges[i].u<=edges[j].v&&edges[i].v>=edges[j].v&&edges[i].u>=edges[j].u) )
            {
                TS.add_clause(i,0,j,0);
                TS.add_clause(i,1,j,1);
            }
    if(TS.solve()) printf("panda is telling the truth...\n");
    else printf("the evil panda is lying again\n");
    return 0;
}


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值