2-SAT 建图总结

2-SAT:

给定一个由布尔值组成的有 n 个元素的序列 A,再有若干限制,每个限制针对至多两个元素,求满足所有限制的序列 A 或者判定不存在。

常见的限制条件:

记每个限制条件给定 a   o p   b ,   a 0 ,   b 0 ,   a 1 ,   b 1 a~op~b,~a_0,~b_0,~a_1,~b_1 a op b, a0, b0, a1, b1 分别代表布尔取值为 0 或 1。
a → b a\rightarrow b ab 代表 a 向 b 连边,表示由 a a a 推导出 b b b
①   a = 1 ,   a 0 → a 1 ①~a=1,~a_0\rightarrow a_1  a=1, a0a1
      a = 0 ,   a 1 → a 0 ~~~~~a=0,~a_1\rightarrow a_0      a=0, a1a0
②   a   &   b = 1 ,   a 0 → a 1 ,   b 0 → b 1 ②~a~\&~b=1,~a_0\rightarrow a_1,~b_0\rightarrow b_1  a & b=1, a0a1, b0b1
      a   &   b = 0 ,   a 1 → b 0 ,   b 1 → a 0 ~~~~~a~\&~b=0,~a_1\rightarrow b_0,~b_1\rightarrow a_0      a & b=0, a1b0, b1a0
③   a   ∣   b = 1 ,   a 0 → b 1 ,   b 0 → a 1 ③~a~\mid~b=1,~a_0\rightarrow b_1,~b_0\rightarrow a_1  a  b=1, a0b1, b0a1
      a   ∣   b = 0 ,   a 1 → a 0 ,   b 1 → b 0 ~~~~~a~\mid~b=0,~a_1\rightarrow a_0,~b_1\rightarrow b_0      a  b=0, a1a0, b1b0
④   a   ⊕ b = 1 ,   a 0 → b 1 ,   a 1 → b 0 ,   b 0 → a 1 ,   b 1 → a 0 ④~a~\oplus b=1,~a_0\rightarrow b_1,~a_1\rightarrow b_0,~b_0\rightarrow a_1,~b_1\rightarrow a_0  a b=1, a0b1, a1b0, b0a1, b1a0
      a   ⊕ b = 0 ,   a 0 → b 0 ,   a 1 → b 1 ,   b 0 → a 0 ,   b 1 → a 1 ~~~~~a~\oplus b=0,~a_0\rightarrow b_0,~a_1\rightarrow b_1,~b_0\rightarrow a_0,~b_1\rightarrow a_1      a b=0, a0b0, a1b1, b0a0, b1a1

两种算法:

①   d f s ,   O ( n m ) ①~dfs,~O(nm)  dfs, O(nm),实际上跑得飞快,可以求出字典序最小的解。
②   t a r j a n ,   O ( n + m ) ②~tarjan,~O(n+m)  tarjan, O(n+m),强连通分量缩点,无解当且仅当存在 a 0 ,   a 1 a_0,~a_1 a0, a1 在同一个强连通分量里。对于解的输出,缩点后,若 b e l [ a 0 ] → b e l [ a 1 ] bel[a_0]\rightarrow bel[a_1] bel[a0]bel[a1],则选择 a 1 a_1 a1,即缩点后按反拓扑序构造解,而缩点后实则已经是反拓扑序,对于 a 0 ,   a 1 a_0,~a_1 a0, a1 选择缩点后编号较小的一个即可。

相关题目:

https://vjudge.net/problem/HDU-1814 求字典序最小的解
https://vjudge.net/problem/HDU-4421 各种位运算限制建图
https://vjudge.net/problem/Gym-101987K t a r j a n tarjan tarjan 缩点输出解

参考代码:
#include<bits/stdc++.h> // HDU-1814

using namespace std;
typedef long long ll;
typedef pair<int, int> pii;
#define pb push_back
#define sz(a) ((int)a.size())
#define mem(a, b) memset(a, b, sizeof a)
#define lson (rt << 1)
#define rson (rt << 1 | 1)
#define gmid (l + r >> 1)
const int maxn = 1e5 + 5;
const int maxm = 1e6 + 5;
const int mod = 1e9 + 7;
const int inf = 0x3f3f3f3f;

vector<int> G[maxn];
int vis[maxn], stk[maxn];
int n, m, top;

int dfs(int u){

	if(vis[u ^ 1]) return 0;
	if(vis[u]) return 1;
	vis[u] = 1, stk[++top] = u;
	for(int i = 0; i < sz(G[u]); ++i){

		int v = G[u][i];
		if(!dfs(v)) return 0;
	}
	return 1;
}

int bisat(){

	for(int i = 0; i < n; ++i) vis[i] = 0;
	for(int i = 0; i < n; ++i){

		if(vis[i] || vis[i ^ 1]) continue;
		top = 0;
		if(!dfs(i)){

			while(top) vis[stk[top--]] = 0;
			if(!dfs(i ^ 1)) return 0;
		}
	}
	return 1;
}

int main() {

	while(scanf("%d%d", &n, &m) != EOF){

		n <<= 1;
		for(int i = 0; i < n; ++i) G[i].clear();
		while(m--){

			int x, y; scanf("%d%d", &x, &y);
			--x, --y;
			G[x].pb(y ^ 1), G[y].pb(x ^ 1);
		}
		for(int i = 0; i < n; ++i) sort(G[i].begin(), G[i].end());
		int ret = bisat();
		if(!ret) printf("NIE\n");
		else{

			for(int i = 0; i < n; ++i) if(vis[i]) printf("%d\n", i + 1);
		}
	}
    return 0;
}

 

#include<bits/stdc++.h> // HDU-4421

using namespace std;
typedef long long ll;
typedef pair<int, int> pii;
#define pb push_back
#define sz(a) ((int)a.size())
#define mem(a, b) memset(a, b, sizeof a)
#define lson (rt << 1)
#define rson (rt << 1 | 1)
#define gmid (l + r >> 1)
const int maxn = 1e3 + 5;
const int maxm = 1e6 + 5;
const int mod = 1e9 + 7;
const int inf = 0x3f3f3f3f;

vector<int> G[maxn];
int dfn[maxn], low[maxn], stk[maxn], ins[maxn], bel[maxn];
int b[maxn][maxn];
int tot, tim, top, n, m;

void tarjan(int u){

	dfn[u] = low[u] = ++tim, stk[++top] = u, ins[u] = 1;
	for(int i = 0; i < sz(G[u]); ++i){

		int v = G[u][i];
		if(!dfn[v]) tarjan(v), low[u] = min(low[u], low[v]);
		else if(ins[v]) low[u] = min(low[u], dfn[v]);
	}
	if(low[u] == dfn[u]){

		int v; ++tot;
		do v = stk[top--], ins[v] = 0, bel[v] = tot; while(v != u);
	}
}

void init(){

	for(int i = 0; i < n; ++i) G[i].clear(), dfn[i] = 0;
	tot = tim = top = 0;
}

int solve(){

	for(int i = 0; i < n; ++i) if(!dfn[i]) tarjan(i);
	for(int i = 0; i < n; ++i) if(bel[i] == bel[i ^ 1]) return 0;
	return 1;
}

int main() {

	while(scanf("%d", &n) != EOF){

		for(int i = 0; i < n; ++i){

			for(int j = 0; j < n; ++j){

				scanf("%d", &b[i][j]);
			}
		}
		int ret = 1; m = n, n <<= 1;
		for(int i = 0; i < 31; ++i){

			init();
			for(int j = 0; j < m; ++j){

				for(int k = 0; k < m; ++k){

					if(j == k) continue;
					int t = (b[j][k] >> i) & 1;
					int x = j << 1, y = k << 1;
					if((j & 1) && (k & 1)){

						if(t) G[x].pb(y ^ 1), G[y].pb(x ^ 1);
						else G[x ^ 1].pb(x), G[y ^ 1].pb(y);
					}
					else if((~j & 1) && (~k & 1)){

						if(t) G[x].pb(x ^ 1), G[y].pb(y ^ 1);
						else G[x ^ 1].pb(y), G[y ^ 1].pb(x);
					}
					else{

						if(t) G[x].pb(y ^ 1), G[x ^ 1].pb(y), G[y].pb(x ^ 1), G[y ^ 1].pb(x);
						else G[x].pb(y), G[x ^ 1].pb(y ^ 1), G[y].pb(x), G[y ^ 1].pb(x ^ 1);
					}
				}
			}
			ret &= solve();
			if(!ret) break;
		}
		printf("%s\n", ret ? "YES" : "NO");
	}
    return 0;
}

 
每人至少猜中两个,即至多猜错一个,设 a ,   b ,   c a,~b,~c a, b, c 分别为三次是否猜中,则 a ∣ b = 1 ,   a ∣ c = 1 ,   b ∣ c = 1 a\mid b=1,~a\mid c=1,~b\mid c=1 ab=1, ac=1, bc=1
 

#include<bits/stdc++.h> // Gym-101987K

using namespace std;
typedef long long ll;
typedef pair<int, int> pii;
#define pb push_back
#define sz(a) ((int)a.size())
#define mem(a, b) memset(a, b, sizeof a)
#define lson (rt << 1)
#define rson (rt << 1 | 1)
#define gmid (l + r >> 1)
const int maxn = 1e5 + 5;
const int maxm = 1e6 + 5;
const int mod = 1e9 + 7;
const int inf = 0x3f3f3f3f;

vector<int> G[maxn];
int dfn[maxn], low[maxn], stk[maxn], ins[maxn], bel[maxn];
int tot, top, tim, n, m;

void tarjan(int u){

	dfn[u] = low[u] = ++tim, stk[++top] = u, ins[u] = 1;
	for(int i = 0; i < sz(G[u]); ++i){

		int v = G[u][i];
		if(!dfn[v]) tarjan(v), low[u] = min(low[u], low[v]);
		else if(ins[v]) low[u] = min(low[u], dfn[v]);
	}
	if(low[u] == dfn[u]){

		int v; ++tot;
		do v = stk[top--], ins[v] = 0, bel[v] = tot;
		while(v != u);
	}
}

void init(){

	for(int i = 0; i < n; ++i) dfn[i] = 0, G[i].clear();
	tot = tim = top = 0;
}

int solve(){

	for(int i = 0; i < n; ++i) if(!dfn[i]) tarjan(i);
	for(int i = 0; i < n; i += 2) if(bel[i] == bel[i ^ 1]) return 0;
	return 1;
}

int main() {

	scanf("%d%d", &n, &m);
	n <<= 1, init();
	while(m--){

		int x, y, z; char sx[2], sy[2], sz[2];
		scanf("%d%s%d%s%d%s", &x, sx, &y, sy, &z, sz);
		--x, --y, --z, x <<= 1, y <<= 1, z <<= 1;
		x |= sx[0] == 'B', y |= sy[0] == 'B', z |= sz[0] == 'B';
		G[x ^ 1].pb(y), G[x ^ 1].pb(z);
		G[y ^ 1].pb(x), G[y ^ 1].pb(z);
		G[z ^ 1].pb(x), G[z ^ 1].pb(y);
	}
	int ret = solve();
	if(!ret) printf("-1\n");
	else{

		for(int i = 0; i < n; i += 2) printf("%c", bel[i] < bel[i ^ 1] ? 'R' : 'B');
		printf("\n");
	}
    return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值