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

题目:

在一个圈上,数字按照顺时针方向,从0-n。有m条边,每条边连接一对点,这每条边可以画在圆的外侧,也可以画在圆的内侧,但是不能相交。

输入会给出点的个数N,和边的条数M,求解知否有可能满足所有边不相交的条件

题目分析:

开始的时候,我没有反应过来是2-SAT的题目。但是题目中有一点是很明显的,那就是边的位置只有两种,一种圆内,一种圆外,那么两条边如果在圆的同侧(同为内侧或外侧)相交的话,就要一定要把他们的位置分开,即使得一条边在内,另一条在外。这样就是很明显的2-SAT的题目了。那么接下来就是数据结构,很显然,我们之前的分析都是以边为单位的,那么就将每条边看做是一个点,每个点的两个状态就是内和外。

那么怎么判断两条边同侧是否相交呢?画图可知,选择两条边中的任意一条边,将圆分成两部分,如果另一条边的两个端点在这条边的同侧,那么不相交,否则(即两个端点在这条边的两侧),必相交。

那么总体来讲,就是按照上述的条件建图,然后用2-SAT的模板就可以了

代码:

//2-Sat
#include <cstdio>
#include <cstring>
#include <algorithm>
using namespace std;

const int N = 1010;
const int M = 1000010;
int n, m, Tcnt, Bcnt, id1, id2;
int head1[N], head2[N], belong[N], T[N];
bool vis1[N], vis2[N];
struct Node {
    int x, y;
}s[N];
struct edge{
    int to, next;
}e1[M], e2[M];

void add( int i, int j ) {
    e1[id1].to = j, e1[id1].next = head1[i], head1[i] = id1++;
    e2[id2].to = i, e2[id2].next = head2[j], head2[j] = id2++;
}
void dfs( int x ) {
    vis1[x] = true;
    for ( int j = head1[x]; j != -1; j = e1[j].next ) if ( !vis1[e1[j].to] ) dfs( e1[j].to );
    T[Tcnt++] = x;
}
void rdfs( int x ) {
    vis2[x] = true;
    belong[x] = Bcnt;
    for ( int j = head2[x]; j != -1; j = e2[j].next ) if ( !vis2[e2[j].to] ) rdfs( e2[j].to );
}
bool solve() {
    for ( int i = 0; i < m*2; ++i ) if ( !vis1[i] ) dfs( i );
    for ( int i = Tcnt -1; i >= 0; i-- ) if ( !vis2[T[i]] ) {
        rdfs( T[i] ); Bcnt++;
    }
    for ( int i = 0; i < m*2; i += 2 ) 
        if ( belong[i] == belong[i+1] ) return false;
    return true;
}
int main()
{
    memset( head1, -1, sizeof(head1) );
    memset( head2, -1, sizeof(head2) );
    memset( vis1, 0, sizeof(vis1) );
    memset( vis2, 0, sizeof(vis2) );
    id1 = id2 = Tcnt = Bcnt = 0;   
    scanf("%d%d", &n, &m);
    for ( int i = 0, a, b; i < m; ++i ) {
        scanf("%d%d", &a, &b);
        s[i*2].x = s[i*2+1].x = min( a, b );
        s[i*2].y = s[i*2+1].y = max( a, b );
    }
    for ( int i = 0; i < m*2-2; ++i ) {
        int t;
        if ( i % 2 == 0 ) t = i + 2;
        else t = i + 1;
        //printf("%d %d\n", i, t);
        for ( int j = t; j < m*2; ++j ) 
            if ( ( (s[j].x > s[i].x && s[j].x < s[i].y && s[j].y > s[i].y ) || ( s[j].y < s[i].y && s[j].y > s[i].x && s[j].x < s[i].x )) && i%2==j%2) {
                //printf("%d %d \n%d %d#\n", i, j^1, j, i^1);
                add( i, j^1 );
                add( j, i^1 );
            }
    }
    if ( solve() ) printf("panda is telling the truth...\n");
    else printf("the evil panda is lying again\n");
}


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值