题意:中文题,有n对夫妻被邀请参加一个聚会,因为场地的问题,每对夫妻中只有1人可以列席。在2n 个人中,某些人之间有着很大的矛盾(当然夫妻之间是没有矛盾的),有矛盾的2个人是不会同时出现在聚会上的。有没有可能会有n 个人同时列席?
SAT是适定性(Satisfiability)问题的简称 。一般形式为k-适定性问题,简称 k-SAT。
当k>2时,k-SAT是NP完全的。因此一般讨论的是k=2的情况,即2-SAT问题。
2-SAT,简单的说就是给出n个集合,每个集合有两个元素,
已知若干个 < a,b >,表示a与b矛盾(其中a与b属于不同的集合)。
然后从每个集合选择一个元素,一共选n个两两不矛盾的元素。
显然可能有多种选择方案,一般题中只需要求出一种即可。
2-SAT问题
构造一个有向图G,每个变量xi拆成两个点2i和2i+1
分别表示xi为假,xi为真
那么对于“xi为真或xj为假”这样的条件
我们就需要连接两条边
2*i —>2*j(表示如果i为假,那么j必须是假) (重点)
2*j+1—>2*i+1(表示如果j为真,那么i必须是真)
这就有点像推导的过程
实际上每一个限制条件都会对应两条“对称”的边
接下来逐考虑每个没有赋值的变量,设为x
我们先假定x为假(为什么一定是假,约定俗成了)
之后沿着从ta出发的有向边进行标记
如果在标记过程中,发现有一个点的两种状态都被标记过了
那么我们之前的假设就被推翻了
需要改成x为真,重新标记
如果发现无论这个点赋值成真还是假,都会引起矛盾
可以证明这个2-SAT无解
这个问题就是个模板题,加一下边就好了
链接:hdu - 3062
#include <iostream>
#include <algorithm>
#include <cstdio>
#include <cmath>
#include <cstring>
#include <string>
#include <set>
#include <map>
#include <stack>
#include <queue>
#define INF 0x3f3f3f3f
using namespace std;
const int inf = 0x3f3f3f3f;
const int maxn = 100005;
const int maxm = 4000005;
//a<<1 和 (a<<1) + 1。a<<1表示妻子,(a<<1) + 1表示丈夫
//连接某边是为了推出矛盾。x->y表示选x则必须选y
//注意方向
struct node {
int s, t;
int nxt;
} e[maxm];
int n, m;
int idx, ans, tp, cont;
int dfn[maxn], vis[maxn], low[maxn], head[maxn], st[maxn], belong[maxn];
void init() {
cont = 0;
memset(head, -1, sizeof(head));
memset(vis, 0, sizeof(vis));
memset(dfn, 0, sizeof(dfn));
idx = tp = ans = 0;
}
void add(int s, int t) {
e[cont].s = s;
e[cont].t = t;
e[cont].nxt = head[s];
head[s] = cont++;
}
void tarjan(int u) {
dfn[u] = low[u] = ++idx;
vis[u] = 1;
st[++tp] = u;
int v ;
for(int i = head[u]; i != -1; i = e[i].nxt) {
v = e[i].t ;
if(!dfn[v]) {
tarjan(v) ;
low[u] = min(low[u],low[v]);
}
else if(vis[v])
low[u] = min(low[u],dfn[v]);
}
if(dfn[u] == low[u]) {
ans++;
while(1) {
v = st[tp--];
vis[v] = 0;
belong[v] = ans;
if(v == u)
break;
}
}
}
bool _2sat() {
for(int i = 0; i < 2 * n; i++)
if(!dfn[i])
tarjan(i) ;
for(int i = 0; i < n; i++)
if(belong[2 * i] == belong[(2 * i) ^ 1])//矛盾
return false;
return true;
}
int main()
{
int a, b, c, d;
while(~scanf("%d %d",&n, &m)) {
init();
for(int i = 0; i < m; i++) {
scanf("%d %d %d %d", &a, &b, &c, &d);
//int u, v;
//u = a*2+c; v = b*2+d;
//add(u, v^1); add(v, u^1);
if(c == 0 && d == 0) {//两个妻子
add(a << 1, (b << 1) + 1);
add(b << 1, (a << 1) + 1);
}
else if(c == 0 && d == 1) {//妻子和丈夫
add((a << 1), (b << 1));
add((b << 1) + 1, (a << 1) + 1);
}
else if(c == 1 && d == 0) {//丈夫和妻子
add((a << 1) + 1, (b << 1) + 1);
add(b << 1, a << 1);
}
else if(c == 1 && d == 1) {//两个丈夫有矛盾
add((a << 1) + 1, b << 1);
add((b << 1) + 1, a << 1);
}
}
if(_2sat())
printf("YES\n");
else
printf("NO\n");
}
return 0 ;
}