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

传送门

题意:有一个圆,圆周上有n个顺序的点,然后有m组边,这些边的两个点必须连起来,圆有两个面,你可以在正面连接他们,也可以在反面连接他们。问是否可以使得这m条线不相交。

解法:因为这些点是按照顺序排序的,在同一个面的情况下,有两对线分别是1 5,2 6,那么就会发现他们肯定是相交的,其实就是一个规律,a[i] < a[j] && a[j] < b[i] && b[i] < b[j].这样肯定是相交的,所以我们遇到这种情况只能将他们放到两面上去。从而转化为2-SAT问题。判断的话就是如果有一面的i和另外一面的i在同一个强联通分量里面那么就说明情况不存在。

2-SAT连的是必须共存的点。对于本题来说,在build中,如果满足相交的条件,那么就是选取了i只能选取j^1,选取了j只能选取i^1.由于有正反两面的性质。所以我们要拆点,每个点都应该对应两个点。

代码如下:

#include<iostream>
#include<cstdio>
#include<vector>
#include<queue>
#include<utility>
#include<stack>
#include<algorithm>
#include<cstring>
#include<string>
#include<cmath>
#include<set>
#include<map>
using namespace std;
const int maxn = 1010 * 2;
const int maxm = 2e6 + 5;
stack <int> sta;
int dfn[maxn], low[maxn], key[maxn], tot = 0, num = 1;
int belong[maxn];
int n, m;
int a[maxn], b[maxn];

int to[maxm], nx[maxm], head[maxn], ppp;
void add_edge(int u, int v) {
	to[ppp] = v, nx[ppp] = head[u], head[u] = ppp++;
}

void tarjan(int u) {
	sta.push(u);
	dfn[u] = low[u] = tot++;
	key[u] = 1;
	for(int i = head[u]; ~i; i = nx[i]) {
		int v = to[i];
		if(dfn[v] == -1) {
			tarjan(v);
			low[u] = min(low[u], low[v]);
		} else if(key[v] == 1) {
			low[u] = min(low[u], dfn[v]);
		}
	}
	if(dfn[u] == low[u]) {
		while(1) {
			int v = sta.top();
			sta.pop();
			belong[v] = num;
			key[v] = 2;
			if(v == u)
				break;
		}
		num++;
	}
}

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_edge(2 * i - 1, 2 * j);
				add_edge(2 * j, 2 * i - 1);
				add_edge(2 * j - 1, 2 * i);
				add_edge(2 * i, 2 * j - 1);
			}
		}
	}
}

bool Check() {
	for(int i = 1; i <= m; i++)
		if(belong[2 * i] == belong[2 * i - 1]) 
			return 0;
	return 1;
}

int main() {
#ifndef ONLINE_JUDGE
//	freopen("in.txt", "r", stdin);
//    freopen("out.txt", "w", stdout);
#endif
	memset(head, -1, sizeof(head));
	memset(dfn, -1, sizeof(dfn));
	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();
//	cout << "hello" << '\n';
	for(int i = 1; i <= 2 * m; i++) {
		if(dfn[i] == -1)
			tarjan(i);
	}
	if(Check())
		printf("panda is telling the truth...\n");
	else
		printf("the evil panda is lying again\n");
	return 0;
}


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值