2-SAT专栏

2-SAT很多时候的应用场景是,给定一个事物,这个事物有两种状态选择。基本就是这个情况,详细的话,通过下面的样例与题集来进行更深入的理解。
例题:
Party-HDU 3062
Katu Puzzle-POJ 3678
实战:
Get Luffy Out *-HDU1816
Go Deeper-HDU 3715
中等题:
Eliminate the Conflict-HDU 4115
Bit Magic-HDU 4421


Party-HDU 3062

传送门
这道题有两种矛盾关系:

  • 一对夫妻中只能一人出席
  • 有矛盾的两人不能同时出席

首先根据第一个矛盾关系,可以确定是2-SAT的题目,假设一对夫妻就是一种事物,只能丈夫去或者妻子去,便是两种选择。其次再看第二个关系,有矛盾的不能同时出席, i . e . i.e. i.e. 1号丈夫和2号妻子不能同时出席,那么如果1号丈夫出席了,那另一对中只能够2号丈夫去;如果2号妻子去了,那另一对中只能1号妻子去。但反过来看,如果1号妻子去了,2号夫妇谁去都无所谓;同样如果2号丈夫去了,1号夫妇谁去也无所谓。

2-SAT就是对矛盾关系中进行建边,当有矛盾关系的两个事物中,第一个事物的一项选择选上的话,另一个事物只能被迫选其中一项才能成立的时候,这时就需要进行建边引导。像上面如果1号妻子去了,不需要进行建边,2号选谁都没问题,这个时候1号妻子去了必定有解。

根据夫妻编号是从0~n-1,现假设建边的时候夫妻编号是 x x x,妻子是 ( x < < 1 ) (x<<1) (x<<1),丈夫是 ( x < < 1 ∣ 1 x<<1|1 x<<11) 。

  • 题目的建边便是 a d d ( A 1 < < 1 ∣ C 1 , A 2 < < 1 ∣ C 2 ⊕ 1 ) add(A1<<1|C1,A2<<1|C2\oplus1) add(A1<<1C1,A2<<1C21), a d d ( A 2 < < 1 ∣ C 2 , A 1 < < 1 ∣ C 1 ⊕ 1 ) add(A2<<1|C2,A1<<1|C1\oplus1) add(A2<<1C2,A1<<1C11)
  • 其中 A 1 < < 1 ∣ C 1 A1<<1|C1 A1<<1C1 就是表示夫妻编号为 A 1 A1 A1当中的妻子或者丈夫,异或就是取对立面,比如 A 2 A2 A2中的丈夫的话,异或后便是 A 2 A2 A2中的妻子。
  • 建边完成后跑一遍 2 − S A T 2-SAT 2SAT 的板子即可。
#include <cstdio>
#include <cstring>
#include <queue>
#include <cmath>
#include <set>
#include <cstdlib>
#include <iostream>
#include <map>
using namespace std;
typedef long long ll;
typedef unsigned long long ull;
typedef unsigned int uii;
typedef pair<int, ll> pii;
template<typename T>
inline void rd(T& x)
{
    
    
	int tmp = 1; char c = getchar(); x = 0;
	while (c > '9' || c < '0') {
    
     if (c == '-')tmp = -1; c = getchar(); }
	while (c >= '0' && c <= '9') {
    
     x = x * 10 + c - '0'; c = getchar(); }
	x *= tmp;
}
const int N = 2e5 + 10;
const int M = 1e6 + 10;
const int mod = 1e9 + 7;
const int inf = 0x3f3f3f3f;
const double eps = 1e-5;
int sgn(double x) {
    
    
	if (fabs(x) < eps) return 0;
	if (x > 0) return 1;
	return -1;
}
int n, m, st, ed;
int head[N], cntE;
struct edge {
    
    
	int to, next;
	int w;
}e[M];
void add(int u, int v, int w = 0) {
    
    
	e[cntE].to = v;
	e[cntE].next = head[u];
	e[cntE].w = w;
	head[u] = cntE++;
}
bool vis[N];
int stk[N], top;
bool dfs(int x) {
    
    
	if (vis[x ^ 1]) return false;
	if (vis[x]) return true;
	vis[x] = true;
	stk[++top] = x;
	for (int i = head[x]; ~i; i = e[i].next) {
    
    
		int v = e[i].to;
		if (!dfs(v)) return false;
	}
	return true;
}
bool two_sat() {
    
    
	memset(vis, false, sizeof(bool) * (n * 2 + 10));
	for (int i = 0; i < n; ++i) {
    
    
		if (vis[i << 1] || vis[i << 1 | 1]) continue;
		top = 0;
		if (!dfs(i << 1)) {
    
    
			while (top) vis[stk[top--]] = false;
			if (!dfs(i << 1 | 1)) return false;
		}
	}
	return true;
}
int main() {
    
    
#ifdef _DEBUG
	FILE* _INPUT = freopen("input.txt", "r", stdin);
	//	FILE* _OUTPUT = freopen("output.txt", "w", stdout);
#endif // !_DEBUG
	int cas = 0;
	while (~scanf("%d", &n)) {
    
    
		rd(m);
		memset(head, -1, sizeof(int) * (n * 2 + 10)); cntE = 0;
		while (m--) {
    
    
			int x, y, i, j; rd(x), rd(y), rd(i), rd(j);
			add(x << 1 | i, y << 1 | j ^ 1);
			add(y << 1 | j, x << 1 | i ^ 1);
		}
		if (two_sat()) puts("YES");
		else puts("NO");
	}
	return 0;
}

Katu Puzzle

  • 题意是给定一个数组 x x x 的长度为 n n n 。然后给出 x i x_i xi o p op op x j = c x_j=c xj=c,其中 c = 0 或 者 1 c=0或者1 c=01,然后 o p op op A N D AND AND O R OR OR X O R XOR XOR
  • 假设数组下标为 i i i ,则 ( i < < 1 ) (i<<1) (i<<1) 表示 x i = 0 x_i=0 xi=0 ( i < < 1 ∣ 1 ) (i<<1|1) (i<<11) 表示 x i = 1 x_i=1 xi=1
  • x i & x j = 1 x_i\&x_j=1 xi&xj=1,显然只能两者同时为1才有解: a d d ( i < < 1 ∣ 1 , j < < 1 ∣ 1 ) , a d d ( j < < 1 ∣ 1 , i < < 1 ∣ 1 ) add(i<<1|1,j<<1|1),add(j<<1|1,i<<1|1) add(i<<11,j<<11),add(j<<11,i<<11);若其中一个为0,则无解,无解让他自相矛盾即可: a d d ( i < < 1 , i < < 1 ∣ 1 ) , a d d ( j < < 1 , j < < 1 ∣ 1 ) add(i<<1,i<<1|1),add(j<<1,j<<1|1) add(i<<1,i<<11),add(j<<1,j<<11);
  • x i & x j = 0 x_i\&x_j=0 xi&xj=0,若 x i = 1 x_i=1 xi=1,只能令 x j = 0 x_j=0 xj=0 才有解: a d d ( i < < 1 ∣ 1 , j < < 1 ) , a d d ( j < < 1 ∣ 1 , i < < 1 ) add(i<<1|1,j<<1),add(j<<1|1,i<<1) add(i<<11,j<<1),add(j<<11,i<<1);若 x i = 0 x_i=0 xi=0,则不管 x j x_j xj 为0或者为1,答案都可行,所以无需建边。 2 − S A T 2-SAT 2SAT 只对唯一情况建边。
  • 同样或运算和异或运算建边一个道理。
#include <cstdio>
#include <cstring>
#include <queue>
#include <cmath>
#include <set>
#include <cstdlib>
#include <iostream>
#include <map>
#include <algorithm>
using namespace std;
typedef long long ll;
typedef unsigned long long ull;
typedef unsigned int uii;
typedef pair<int, ll> pii;
template<typename T>
inline void rd(T& x)
{
    
    
	int tmp = 1; char c = getchar(); x = 0;
	while (c > '9' || c < '0') {
    
     if (c == '-')tmp = -1; c = getchar(); }
	while (c >= '0' && c <= '9') {
    
     x = x * 10 + c - '0'; c = getchar(); }
	x *= tmp;
}
const int N = 2e5 + 10;
const int M = 1e6 + 10;
const int mod = 1e9 + 7;
const int inf = 0x3f3f3f3f;
const double eps = 1e-5;
int sgn(double x) {
    
    
	if (fabs(x) < eps) return 0;
	if (x > 0) return 1;
	return -1;
}
int n, m, st, ed, k;
int head[N], cntE;
struct edge {
    
    
	int to, next;
	int w;
}e[M];
void add(int u, int v, int w = 0) {
    
    
	e[cntE].to = v;
	e[cntE].next = head[u];
	e[cntE].w = w;
	head[u] = cntE++;
}
bool vis[N];
int stk[N], top;
bool dfs(int x) {
    
    
	if (vis[x ^ 1]) return false;
	if (vis[x]) return true;
	vis[x] = true;
	stk[++top] = x;
	for (int i = head[x]; ~i; i = e[i].next) {
    
    
		int v = e[i].to;
		if (!dfs(v)) return false;
	}
	return true;
}
bool two_sat() {
    
    
	memset(vis, false, sizeof(bool) * (2 * n + 10));
	for (int i = 0; i < n; ++i) {
    
    
		if (vis[i << 1] || vis[i << 1 | 1]) continue;
		top = 0;
		if (!dfs(i << 1)) {
    
    
			while (top) vis[stk[top--]] = false;
			if (!dfs(i << 1 | 1)) return false;
		}
	}
	return true;
}
int main() {
    
    
#ifdef _DEBUG
	FILE* _INPUT = freopen("input.txt", "r", stdin);
	//	FILE* _OUTPUT = freopen("output.txt", "w", stdout);
#endif // !_DEBUG
	int cas = 0, T;
	//	rd(T);
	//	while (T--) {
    
    
	while (~scanf("%d %d", &n, &m)) {
    
    
		memset(head, -1, sizeof(int) * (2 * n + 10)); cntE = 0;
		while (m--) {
    
    
			int x, y, c; char op[5];
			scanf("%d %d %d %s", &x, &y, &c, op);
			if (op[0] == 'A') {
    
    
				if (c) {
    
    
					add(x << 1 | 1, y << 1 | 1); add(y << 1 | 1, x << 1 | 1);
					add(x << 1, x << 1 | 1); add(y << 1, y << 1 | 1);
				}
				else {
    
    
					add(x << 1 | 1, y << 1);
					add(y << 1 | 1, x << 1);
				}
			}
			else if (op[0] == 'O') {
    
    
				if (c) {
    
    
					add(x << 1, y << 1 | 1);
					add(y << 1, x << 1 | 1);
				}
				else {
    
    
					add(x << 1, y << 1); add(y << 1, x << 1);
					add(x << 1 | 1, x << 1);
					add(y << 1 | 1, y << 1);
				}
			}
			else {
    
    
				if (c) {
    
    
					add(x << 1, y << 1 | 1); add(y << 1 | 1, x << 1);
					add(x << 1 | 1, y << 1); add(y << 1, x << 1 | 1);
				}
				else {
    
    
					add(x << 1, y << 1); add(y << 1, x << 1);
					add(x << 1 | 1, y << 1 | 1); add(y << 1 | 1, x << 1 | 1);
				}
			}
		}
		if (two_sat()) puts("YES");
		else puts("NO");
	}
	return 0;
}

Get Luffy Out *

传送门

  • 这道题的做法,显然是二分答案+ 2 − S A T 2-SAT 2SAT
  • 建边关系有:假设一对钥匙下标为 x 与 y x与y xy ( x < < 1 ) 表 示 x 未 使 用 , ( x < < 1 ∣ 1 ) 表 示 钥 匙 使 用 了 (x<<1)表示x未使用,(x<<1|1)表示钥匙使用了 (x<<1)x使(x<<11)使,使用其中一把,另一把就会消失,所以建边 a d d ( x < < 1 ∣ 1 , y < < 1 ) , a d d ( y < < 1 ∣ 1 , x < < 1 ) add(x<<1|1,y<<1),add(y<<1|1,x<<1) add(x<<11,y<<1),add(y<<11,x<<1)
  • 下面开门中,选择其中一个开门即可,所以要把门打开,必须有钥匙匹配,若其中一把钥匙不匹配,另一把必定要匹配到才有解: a d d ( x < < 1 , y < < 1 ∣ 1 ) , a d d ( y < < 1 , x < < 1 ∣ 1 ) add(x<<1,y<<1|1),add(y<<1,x<<1|1) add(x<<1,y<<11),add(y<<1,x<<11) ;若有钥匙可以使用,则另一把钥匙用不用都无所谓,所以无需建边,保证有解即可。
  • 接下来就是二分能够到达的层数即可
#include <cstdio>
#include <cstring>
#include <queue>
#include <cmath>
#include <set>
#include <cstdlib>
#include <iostream>
#include <map>
using namespace std;
typedef long long ll;
typedef unsigned long long ull;
typedef unsigned int uii;
typedef pair<int, ll> pii;
template<typename T>
inline void rd(T& x)
{
    
    
	int tmp = 1; char c = getchar(); x = 0;
	while (c > '9' || c < '0') {
    
     if (c == '-')tmp = -1; c = getchar(); }
	while (c >= '0' && c <= '9') {
    
     x = x * 10 + c - '0'; c = getchar(); }
	x *= tmp;
}
const int N = 2e5 + 10;
const int M = 1e6 + 10;
const int mod = 1e9 + 7;
const int inf = 0x3f3f3f3f;
const double eps = 1e-5;
int sgn(double x) {
    
    
	if (fabs(x) < eps) return 0;
	if (x > 0) return 1;
	return -1;
}
int n, m, st, ed, k;
int head[N], cntE;
struct edge {
    
    
	int to, next;
	int w;
}e[M];
void add(int u, int v, int w = 0) {
    
    
	e[cntE].to = v;
	e[cntE].next = head[u];
	e[cntE].w = w;
	head[u] = cntE++;
}
bool vis[N];
int stk[N], top;
bool dfs(int x) {
    
    
	if (vis[x ^ 1]) return false;
	if (vis[x]) return true;
	vis[x] = true;
	stk[++top] = x;
	for (int i = head[x]; ~i; i = e[i].next) {
    
    
		int v = e[i].to;
		if (!dfs(v)) return false;
	}
	return true;
}
bool two_sat() {
    
    
	memset(vis, false, sizeof(bool) * (n * 4 + 10));
	for (int i = 0; i < n * 2; ++i) {
    
    
		if (vis[i << 1] || vis[i << 1 | 1]) continue;
		top = 0;
		if (!dfs(i << 1)) {
    
    
			while (top) vis[stk[top--]] = false;
			if (!dfs(i << 1 | 1)) return false;
		}
	}
	return true;
}
int a[N], b[N], c[N], d[N];
bool check(int x) {
    
    
	memset(head, -1, sizeof(int) * (n * 4 + 10)); cntE = 0;
	for (int i = 1; i <= n; ++i) {
    
    
		add(a[i] << 1 | 1, b[i] << 1);
		add(b[i] << 1 | 1, a[i] << 1);
	}
	for (int i = 1; i <= x; ++i) {
    
    
		add(c[i] << 1, d[i] << 1 | 1);
		add(d[i] << 1, c[i] << 1 | 1);
	}
	if (two_sat()) return true;
	else return false;
}
int main() {
    
    
#ifdef _DEBUG
	FILE* _INPUT = freopen("input.txt", "r", stdin);
	//	FILE* _OUTPUT = freopen("output.txt", "w", stdout);
#endif // !_DEBUG
	int cas = 0, T;
//	rd(T);
//	while (T--) {
    
    
	while (~scanf("%d %d", &n,&m),n+m) {
    
    
		for (int i = 1; i <= n; ++i) {
    
    
			rd(a[i]); rd(b[i]);
		}
		for (int i = 1; i <= m; ++i) {
    
    
			rd(c[i]); rd(d[i]);
		}
		int l = 1, r = m;
		while (l <= r) {
    
    
			int mid = l + r >> 1;
			if (check(mid)) l = mid + 1;
			else r = mid - 1;
		}
		printf("%d\n", l - 1);
	}
	return 0;
}

Go Deeper

传送门

扫描二维码关注公众号,回复: 12817140 查看本文章
  • 同样是二分答案+ 2 − S A T 2-SAT 2SAT ,设 ( x i < < 1 ) 为 x i = 0 , ( x i < < 1 ∣ 1 ) 为 x i = 1 ; (x_i<<1)为x_i=0,(x_i<<1|1)为x_i=1; (xi<<1)xi=0,(xi<<11)xi=1;
  • x i + x j = 0 x_i+x_j=0 xi+xj=0 ,建边 a d d ( i < < 1 , j < < 1 ∣ 1 ) , a d d ( j < < 1 , i < < 1 ∣ 1 ) add(i<<1,j<<1|1),add(j<<1,i<<1|1) add(i<<1,j<<11),add(j<<1,i<<11) ;若其中一个状态为1,另一个无论如何都不可能相加得0,必定有解无须建边。
  • 同样的套路用在其余两个上, 详情见代码
#include <cstdio>
#include <cstring>
#include <queue>
#include <cmath>
#include <set>
#include <cstdlib>
#include <iostream>
#include <map>
using namespace std;
typedef long long ll;
typedef unsigned long long ull;
typedef unsigned int uii;
typedef pair<int, ll> pii;
template<typename T>
inline void rd(T& x)
{
    
    
	int tmp = 1; char c = getchar(); x = 0;
	while (c > '9' || c < '0') {
    
     if (c == '-')tmp = -1; c = getchar(); }
	while (c >= '0' && c <= '9') {
    
     x = x * 10 + c - '0'; c = getchar(); }
	x *= tmp;
}
const int N = 2e5 + 10;
const int M = 1e6 + 10;
const int mod = 1e9 + 7;
const int inf = 0x3f3f3f3f;
const double eps = 1e-5;
int sgn(double x) {
    
    
	if (fabs(x) < eps) return 0;
	if (x > 0) return 1;
	return -1;
}
int n, m, st, ed;
int head[N], cntE;
struct edge {
    
    
	int to, next;
	int w;
}e[M];
void add(int u, int v, int w = 0) {
    
    
	e[cntE].to = v;
	e[cntE].next = head[u];
	e[cntE].w = w;
	head[u] = cntE++;
}
bool vis[N];
int stk[N], top;
bool dfs(int x) {
    
    
	if (vis[x ^ 1]) return false;
	if (vis[x]) return true;
	vis[x] = true;
	stk[++top] = x;
	for (int i = head[x]; ~i; i = e[i].next) {
    
    
		int v = e[i].to;
		if (!dfs(v)) return false;
	}
	return true;
}
bool two_sat() {
    
    
	memset(vis, false, sizeof(bool) * (n * 2 + 10));
	for (int i = 0; i < n; ++i) {
    
    
		if (vis[i << 1] || vis[i << 1 | 1]) continue;
		top = 0;
		if (!dfs(i << 1)) {
    
    
			while (top) vis[stk[top--]] = false;
			if (!dfs(i << 1 | 1)) return false;
		}
	}
	return true;
}
int x[N], y[N], w[N];
bool check(int mid) {
    
    
	memset(head, -1, sizeof(int) * (n * 2 + 10)); cntE = 0;
	for (int i = 1; i <= mid; ++i) {
    
    
		if (w[i] == 0) {
    
    
			add(x[i] << 1, y[i] << 1 | 1);
			add(y[i] << 1, x[i] << 1 | 1);
		}
		else if (w[i] == 1) {
    
    
			add(x[i] << 1, y[i] << 1);
			add(y[i] << 1, x[i] << 1);
			add(x[i] << 1 | 1, y[i] << 1 | 1);
			add(y[i] << 1 | 1, x[i] << 1 | 1);
		}
		else {
    
    
			add(x[i] << 1 | 1, y[i] << 1);
			add(y[i] << 1 | 1, x[i] << 1);
		}
	}
	if (two_sat()) return true;
	else return false;
}
int main() {
    
    
#ifdef _DEBUG
	FILE* _INPUT = freopen("input.txt", "r", stdin);
	//	FILE* _OUTPUT = freopen("output.txt", "w", stdout);
#endif // !_DEBUG
	int cas = 0, T;
	rd(T);
	while (T--) {
    
    
//	while (~scanf("%d", &n)) {
    
    
		rd(n), rd(m);
		for (int i = 1; i <= m; ++i) {
    
    
			rd(x[i]), rd(y[i]), rd(w[i]);
		}
		int l = 1, r = m;
		while (l <= r) {
    
    
			int mid = l + r >> 1;
			if (check(mid)) l = mid + 1;
			else r = mid - 1;
		}
		printf("%d\n", l - 1);
	}
	return 0;
}


Eliminate the Conflict

传送门

  • 这题很难想到 2 − S A T 2-SAT 2SAT 2 − S A T 2-SAT 2SAT 需要用到一个事物的两种状态,而这道题,若想要赢,在满足所有条件的情况下,在每一轮的游戏中:要么赢,要么平局。而这个便是两种不同的状态,赢或者平局。
  • 对应下先处理出每一局的胜利和平局的表示。
  • 对于有约束关系的 i 和 j i和j ij,若其中一个状态不满足,则必须与对面另一个状态建边,把握好这个思想就可以了,详情看代码吧。
#include <cstdio>
#include <cstring>
#include <queue>
#include <cmath>
#include <set>
#include <cstdlib>
#include <iostream>
#include <map>
using namespace std;
typedef long long ll;
typedef unsigned long long ull;
typedef unsigned int uii;
typedef pair<int, ll> pii;
template<typename T>
inline void rd(T& x)
{
    
    
	int tmp = 1; char c = getchar(); x = 0;
	while (c > '9' || c < '0') {
    
     if (c == '-')tmp = -1; c = getchar(); }
	while (c >= '0' && c <= '9') {
    
     x = x * 10 + c - '0'; c = getchar(); }
	x *= tmp;
}
const int N = 2e5 + 10;
const int M = 1e6 + 10;
const int mod = 1e9 + 7;
const int inf = 0x3f3f3f3f;
const double eps = 1e-5;
int sgn(double x) {
    
    
	if (fabs(x) < eps) return 0;
	if (x > 0) return 1;
	return -1;
}
int n, m, st, ed, k;
int head[N], cntE;
struct edge {
    
    
	int to, next;
	int w;
}e[M];
void add(int u, int v, int w = 0) {
    
    
	e[cntE].to = v;
	e[cntE].next = head[u];
	e[cntE].w = w;
	head[u] = cntE++;
}
bool vis[N];
int stk[N], top;
bool dfs(int x) {
    
    
	if (vis[x ^ 1]) return false;
	if (vis[x]) return true;
	vis[x] = true;
	stk[++top] = x;
	for (int i = head[x]; ~i; i = e[i].next) {
    
    
		int v = e[i].to;
		if (!dfs(v)) return false;
	}
	return true;
}
bool two_sat() {
    
    
	memset(vis, false, sizeof(bool) * (n * 2 + 10));
	for (int i = 0; i < n; ++i) {
    
    
		if (vis[i << 1] || vis[i << 1 | 1]) continue;
		top = 0;
		if (!dfs(i << 1)) {
    
    
			while (top) vis[stk[top--]] = false;
			if (!dfs(i << 1 | 1)) return false;
		}
	}
	return true;
}
int a[N][2];
int main() {
    
    
#ifdef _DEBUG
	FILE* _INPUT = freopen("input.txt", "r", stdin);
	//	FILE* _OUTPUT = freopen("output.txt", "w", stdout);
#endif // !_DEBUG
	int cas = 0, T;
	rd(T);
	while (T--) {
    
    
//	while (~scanf("%d %d", &n,&m)) {
    
    
		rd(n), rd(m);
		for (int i = 1; i <= n; ++i) {
    
    
			rd(a[i][0]); --a[i][0];
			a[i][1] = (a[i][0] + 1) % 3;
		}
		memset(head, -1, sizeof(int) * (n * 2 + 10)); cntE = 0;
		while (m--) {
    
    
			int x, y, op; rd(x), rd(y), rd(op);
			if (!op) {
    
    
				for (int i = 0; i < 2; ++i) {
    
    
					for (int j = 0; j < 2; ++j) {
    
    
						if (a[x][i] != a[y][j]) {
    
    
							add(x << 1 | i, y << 1 | j ^ 1);
							add(y << 1 | j, x << 1 | i ^ 1);
						}
					}
				}
			}
			else {
    
    
				for (int i = 0; i < 2; ++i) {
    
    
					for (int j = 0; j < 2; ++j) {
    
    
						if (a[x][i] == a[y][j]) {
    
    
							add(x << 1 | i, y << 1 | j ^ 1);
							add(y << 1 | j, x << 1 | i ^ 1);
						}
					}
				}
			}
			
		}
		printf("Case #%d: ", ++cas);
		if (two_sat()) puts("yes");
		else puts("no");
	}
	return 0;
}

Bit Magic

传送门

  • 这题是可以用到 2 − S A T 2-SAT 2SAT 的,需要用到两种状态的话,很容易想到,对数组b的所有数进行二进制转换。转换后对应1-31位只有0或者1两种状态,应用这个状态进行判定即可。
  • 在建边的详情中与上面第二题原理一样,就不多解释
#include <cstdio>
#include <cstring>
#include <queue>
#include <cmath>
#include <set>
#include <cstdlib>
#include <iostream>
#include <map>
using namespace std;
typedef long long ll;
typedef unsigned long long ull;
typedef unsigned int uii;
typedef pair<int, ll> pii;
template<typename T>
inline void rd(T& x)
{
    
    
	int tmp = 1; char c = getchar(); x = 0;
	while (c > '9' || c < '0') {
    
     if (c == '-')tmp = -1; c = getchar(); }
	while (c >= '0' && c <= '9') {
    
     x = x * 10 + c - '0'; c = getchar(); }
	x *= tmp;
}
const int N = 2e5 + 10;
const int M = 1e6 + 10;
const int mod = 1e9 + 7;
const int inf = 0x3f3f3f3f;
const double eps = 1e-5;
int sgn(double x) {
    
    
	if (fabs(x) < eps) return 0;
	if (x > 0) return 1;
	return -1;
}
int n, m, st, ed, k;
int head[N], cntE;
struct edge {
    
    
	int to, next;
	int w;
}e[M];
void add(int u, int v, int w = 0) {
    
    
	e[cntE].to = v;
	e[cntE].next = head[u];
	e[cntE].w = w;
	head[u] = cntE++;
}
bool vis[N];
int stk[N], top;
bool dfs(int x) {
    
    
	if (vis[x ^ 1]) return false;
	if (vis[x]) return true;
	vis[x] = true;
	stk[++top] = x;
	for (int i = head[x]; ~i; i = e[i].next) {
    
    
		int v = e[i].to;
		if (!dfs(v)) return false;
	}
	return true;
}
bool two_sat() {
    
    
	memset(vis, false, sizeof(bool) * (n * 2 + 10));
	for (int i = 0; i < n; ++i) {
    
    
		if (vis[i << 1] || vis[i << 1 | 1]) continue;
		top = 0;
		if (!dfs(i << 1)) {
    
    
			while (top) vis[stk[top--]] = false;
			if (!dfs(i << 1 | 1)) return false;
		}
	}
	return true;
}
int b[505][505];
bool check() {
    
    
	for (int x = 0; x < 31; ++x) {
    
    
		memset(head, -1, sizeof(int) * (n * 2 + 10)); cntE = 0;
		for (int i = 0; i < n; ++i) {
    
    
			for (int j = i + 1; j < n; ++j) {
    
    
				if ((i & 1) && (j & 1)) {
    
    
					if (b[i][j] & (1 << x)) {
    
    
						add(i << 1, j << 1 | 1);
						add(j << 1, i << 1 | 1);
					}
					else {
    
    
						add(i << 1, j << 1);add(j << 1, i << 1);
						add(i << 1 | 1, i << 1);
						add(j << 1 | 1, j << 1);
					}
				}
				else if (!(i & 1) && !(j & 1)) {
    
    
					if (b[i][j] & (1 << x)) {
    
    
						add(i << 1 | 1, j << 1 | 1); add(j << 1 | 1, i << 1 | 1);
						add(i << 1, i << 1 | 1);
						add(j << 1, j << 1 | 1);
					}
					else {
    
    
						add(i << 1 | 1, j << 1);
						add(j << 1 | 1, i << 1);
					}
				}
				else {
    
    
					if (b[i][j] & (1 << x)) {
    
    
						add(i << 1 | 1, j << 1);add(j << 1 | 1, i << 1);
						add(i << 1, j << 1 | 1);add(j << 1, i << 1 | 1);
					}
					else {
    
    
						add(i << 1, j << 1); add(j << 1, i << 1);
						add(i << 1 | 1, j << 1 | 1); add(j << 1 | 1, i << 1 | 1);
					}
				}
			}
		}
		if (!two_sat()) return false;
	}
	return true;
}
int main() {
    
    
#ifdef _DEBUG
	FILE* _INPUT = freopen("input.txt", "r", stdin);
	//	FILE* _OUTPUT = freopen("output.txt", "w", stdout);
#endif // !_DEBUG
	int cas = 0, T;
//	rd(T);
//	while (T--) {
    
    
	while (~scanf("%d", &n)) {
    
    
		for (int i = 0; i < n; ++i) {
    
    
			for (int j = 0; j < n; ++j) {
    
    
				rd(b[i][j]);
			}
		}
		if (check()) puts("YES");
		else puts("NO");
	}
	return 0;
}

猜你喜欢

转载自blog.csdn.net/bloom_er/article/details/113507150