聚会
题目链接:ybt金牌导航3-6-1
题目大意
有一些夫妇,然后两个人只能一个人去聚会。
然后有一些限制条件限制某些人不能同时去约会。
问你是否存在一种可能使每对都可以派人去。
思路
这道题看到二选一,然后又看到限制条件,而且条件有关两个人,自然会想到 2-SAT。
那你先看看连边怎么连。
那如果出现一个条件,
A
A
A 和
B
B
B 不能同时出现,那就
A
A
A 连向
B
B
B 的另一半,
B
B
B 连向
A
A
A 的另一半。
(因为你一旦选了这两个的其中一个,另一个就一定不能选,就一定只能选它的另一半)
然后就 Tarjan 缩点然后判断即可。
然后讲讲 2-SAT 是怎么的过程。
那你会对于每组有两个状态,根据题目它们肯定是刚好发生其中一个的。
然后如果连边
a
a
a 连向
b
b
b,就是如果推出了
a
a
a,那你可以推出
b
b
b。
那这个是有传递性的。
那你可以想到,如果形成强连通分量,那如果里面的一个推出,那全部都能推出。
那如果在某个强连通分量中出现了某一对的两个点,那就说明矛盾。
(因为它强连通分量说明要么都推出,要么都推不出,但是一对又要求只有一个推出,那就矛盾了)
我们就可以用这种方法来看是否可行。
代码
#include<cstdio>
#include<cstring>
#include<iostream>
using namespace std;
struct node {
int to, nxt;
}e[2000001];
int n, m, le[5001], KK;
int a1, a2, c1, c2, tmp;
int dfn[5001], low[5001];
int sta[5001], n_n, in[5001];
int num[5001];
bool YES;
void csh() {
memset(e, 0, sizeof(e));
memset(le, 0, sizeof(le));
KK = 0;
YES = 1;
memset(dfn, 0, sizeof(dfn));
memset(low, 0, sizeof(low));
memset(sta, 0, sizeof(sta));
n_n = 0;
memset(in, 0, sizeof(in));
memset(num, 0, sizeof(num));
}
int yes(int now) {
return now;
}
int no(int now) {
return n + now;
}
int change(int now) {
if (now <= n) return n + now;
return now - n;
}
void add(int x, int y) {
e[++KK] = (node){y, le[x]}; le[x] = KK;
}
void tarjan(int now) {//Tarjan 缩点
dfn[now] = low[now] = ++tmp;
sta[++sta[0]] = now;
for (int i = le[now]; i; i = e[i].nxt)
if (!dfn[e[i].to]) {
tarjan(e[i].to);
low[now] = min(low[now], low[e[i].to]);
}
else if (!in[e[i].to]) low[now] = min(low[now], low[e[i].to]);
if (dfn[now] == low[now]) {
in[now] = ++n_n;
num[n_n]++;
while (sta[sta[0]] != now) {
in[sta[sta[0]]] = n_n;
num[n_n]++;
sta[0]--;
}
sta[0]--;
}
return ;
}
int main() {
while (scanf("%d", &n) != EOF) {
scanf("%d", &m);
csh();//初始化
for (int i = 1; i <= m; i++) {
scanf("%d %d %d %d", &a1, &a2, &c1, &c2);
a1++;
a2++;
add(a1 + c1 * n, change(a2 + c2 * n));//建图
add(a2 + c2 * n, change(a1 + c1 * n));
}
for (int i = 1; i <= n + n; i++)
if (!dfn[i]) tarjan(i);
for (int i = 1; i <= n; i++)
if (in[yes(i)] == in[no(i)]) {//看是否有矛盾
printf("NO\n");
YES = 0;
break;
}
if (YES) printf("YES\n");
}
return 0;
}