2-SAT

1. 2 − S A T \rm 2-SAT 2SAT 问题简述

n n n 个变量,每个变量有只有 2 2 2 种取值,还有 m m m 个约束条件,每个条件都是对 k k k 个变量的约束。问这 n n n​​ 个变量有没有一种取值方法,能满足这 m m m 个条件,这个问题就是 k − S A T \rm k-SAT kSAT 问题,其中 SAT \text{SAT} SAT satisfiability \text{satisfiability} satisfiability 的缩写,意为“满足性”​。

k > 2 k>2 k>2 时, k − S A T \rm k-SAT kSAT 问题为 N P \rm NP NP 完全问题,只能用暴力;当 k = 2 k=2 k=2 时,我们可以通过 强连通分量(我用了 T a r j a n \rm Tarjan Tarjan) 实现 O ⁡ ( n + m ) \operatorname{O}(n+m) O(n+m)​ 解决。

举个栗子:

现在举行了一场 ⌈ 数 据 删 除 ⌋ \left\lceil 数据删除\right\rfloor ​​ 的比赛,有 3 3 3​​​ 位候选人和 3 3 3​​​ 位评委,每位评委要满足条件之一:

  1. yzh 评委:
    • cxr 进入决赛;
    • wsy 进入决赛。
  2. xhj 评委:
    • wsy 进入决赛;
    • zlq 不进入决赛。
  3. sid 评委:
    • zlq 进入决赛;
    • cxr 进入决赛。

那么我们可以找到一组方案:cxr 不进入决赛,wsy 进入决赛,zlq 进入决赛(完了我又要被揍了啊 /fad)。

2. 2 − S A T \rm 2-SAT 2SAT​ 问题解决

P4782 【模板】2-SAT 问题

题意

n n n 个变量 x 1 ∼ x n ( x i ∈ { 0 , 1 } ) x_1\sim x_n(x_i\in\{0,1\}) x1xn(xi{0,1}),另有 m m m 个需要满足的条件,每个条件给出 i , a , j , b i,a,j,b i,a,j,b,表示 ⌈ x i \lceil x_i xi a a a x j x_j xj b ⌋ b\rfloor b。给每个变量赋值使得所有条件得到满足,若无解,输出 IMPOSSIBLE,否则输出 POSSIBLE 并构造一组解。

思路

先建立有 2 n 2n 2n 个节点的有向图,第 i i i 号节点意味着 x i = 0 x_i=0 xi=0,第 i + n i+n i+n 号节点意味着 x i = 1 x_i=1 xi=1

对于一个约束条件:

  1. a = 0 , b = 0 a=0,b=0 a=0,b=0​​​​​​,则向 i + n → j i+n\to j i+nj​​​​​​ 连边, j + n → i j+n\to i j+ni​​​​​​ 连边,说明当 x i = 1 x_i=1 xi=1​​​​​​ 时 x j x_j xj​​​​​​ 必须取 0 0 0​​​​​​, x j = 1 x_j=1 xj=1 x i x_i xi 必须取 0 0 0​​​​​​​​;
  2. a = 0 , b = 1 a=0,b=1 a=0,b=1​​​​​​,则向 i + n → j + n i+n\to j+n i+nj+n​​​​​​ 连边, j → i j\to i ji​​​​​​ 连边,说明当 x i = 1 x_i=1 xi=1​​​​​​ 时 x j x_j xj​​​​​​ 必须取 1 1 1​​​​​​, x j = 0 x_j=0 xj=0 x i x_i xi 必须取 0 0 0​​​​​​​;
  3. a = 1 , b = 0 a=1,b=0 a=1,b=0​​​​​​​​,则向 i → j i\to j ij​​​​​ 连边, j + n → i + n j+n\to i+n j+ni+n​​​​​ 连边,说明当 x i = 0 x_i=0 xi=0​​​​​ 时 x j x_j xj​​​​​ 必须取 0 0 0​​​​​, x j = 1 x_j=1 xj=1 x i x_i xi 必须取 1 1 1​​​​​​​​​;
  4. a = 1 , b = 1 a=1,b=1 a=1,b=1​​​​​​​​​,则向 i → j + n i\to j+n ij+n​​​​​​​​​ 连边, j → i + n j\to i+n ji+n​​​​​​​​​ 连边,说明当 x i = 0 x_i=0 xi=0​​​​​​​​​ 时 x j x_j xj​​​​​​​​​ 必须取 1 1 1​​​​​​​​​, x j = 0 x_j=0 xj=0​​​​​​ 时 x i x_i xi​​​​​​ 必须取 1 1 1​​​​​​​​​。

建图代码:

while (m--)
{
    int i, a, j, b;
    scanf("%d%d%d%d", &i, &a, &j, &b);
    if (a == 0)
    {
        if (b == 0)
        {
            add(i + n, j);
            add(j + n, i);
        }
        else
        {
            add(i + n, j + n);
            add(j, i);
        }
    }
    else
    {
        if (b == 0)
        {
            add(i, j);
            add(j + n, i + n);
        }
        else
        {
            add(i, j + n);
            add(j, i + n);
        }
    }
}

当然,我们可以简化一下:

while (m--)
{
    int i, a, j, b;
    scanf("%d%d%d%d", &i, &a, &j, &b);
    add(i + a * n, j + (1 - b) * n);
    add(j + b * n, i + (1 - a) * n);
}

建图后,我们求一遍强连通,设点 i i i 所在的强连通的编号为 c i c_i ci,遍历 i = 1 → n i=1\to n i=1n,然后判断:若 c i = c i + n c_i=c_{i+n} ci=ci+n:说明若 x i x_i xi 0 / 1 0/1 0/1,则对应的, x i x_i xi 必须取 1 / 0 1/0 1/0​​???炸了,所以我们推出了矛盾,即无解。

否则说明有解,那么我们要怎么构造解呢?

其实直接取所在强连通的编号更小的那个即可,原因如下:

在用 T a r j a n \rm Tarjan Tarjan 求强连通时,由于是往下搜,所以实际上更晚访问的强连通会被先标记,即该强连通的编号更小。

对于一个节点 i i i,假设它对应的是取 0 0 0,则取 1 1 1 的是 i + n i+n i+n,若有这样一条路

i → j → i + n i\to j\to i+n iji+n

那么 i + n i+n i+n 所在的强连通编号更小。当我们取 i i i​ 时同时会取到 i + n i+n i+n,就不行了,所以我们只能取 i + n i+n i+n​​ 所在的强连通,即编号更小的。

for (int i = 1; i <= n; i++)
{
    printf("%d ", c[i] < c[i + n]);
}

Code \text{Code} Code

#include <iostream>
#include <cstdio>
#include <stack>
using namespace std;

const int MAXN = 2e6 + 5;

int cnt, Time, scc;
int head[MAXN], dfn[MAXN], low[MAXN], c[MAXN];
bool ins[MAXN];
stack<int> s;

struct edge
{
	int to, nxt;
}e[MAXN << 1];

void add(int u, int v) 
{
	e[++cnt] = edge{v, head[u]};
	head[u] = cnt;
}

void tarjan(int u)
{
	dfn[u] = low[u] = ++Time;
	s.push(u);
	ins[u] = true;
	for (int i = head[u]; i; i = e[i].nxt)
	{
		int v = e[i].to;
		if (!dfn[v])
		{
			tarjan(v);
			low[u] = min(low[u], low[v]);
		}
		else if (ins[v])
		{
			low[u] = min(low[u], dfn[v]);
		}
	}
	if (dfn[u] == low[u])
	{
		scc++;
		int v = 0;
		while (v != u)
		{
			v = s.top();
			s.pop();
			c[v] = scc;
			ins[v] = false;
		}
	}
}

int main()
{
	int n, m;
	scanf("%d%d", &n, &m);
	for (int i = 1; i <= m; i++)
	{
		int u, a, v, b;
		scanf("%d%d%d%d", &u, &a, &v, &b);
		add(u + a * n, v + (1 - b) * n);
		add(v + b * n, u + (1 - a) * n);
	}
	for (int i = 1; i <= (n << 1); i++)
	{
		if (!dfn[i])
		{
			tarjan(i);
		}
	}
	for (int i = 1; i <= n; i++)
	{
		if (c[i] == c[i + n])
		{
			puts("IMPOSSIBLE");
			return 0; 
		}
	}
	puts("POSSIBLE");
	for (int i = 1; i <= n; i++)
	{
		printf("%d ", c[i] < c[i + n]);
	}
	return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
毕业设计,基于SpringBoot+Vue+MySQL开发的校园二手书交易平台,源码+数据库+毕业论文+视频演示 信息数据从传统到当代,是一直在变革当中,突如其来的互联网让传统的信息管理看到了革命性的曙光,因为传统信息管理从时效性,还是安全性,还是可操作性等各个方面来讲,遇到了互联网时代才发现能补上自古以来的短板,有效的提升管理的效率和业务水平。传统的管理模式,时间越久管理的内容越多,也需要更多的人来对数据进行整理,并且数据的汇总查询方面效率也是极其的低下,并且数据安全方面永远不会保证安全性能。结合数据内容管理的缺点,在互联网时代都可以得到有效的补充。结合先进的互联网技术,开发符合需求的软件,让数据内容管理不管是从录入的及时性,查看的及时性还是汇总分析的及时性,都能让正确率达到最高,管理更加的科学和便捷。本次开发的校园二手书交易平台实现了图书信息查询。系统用到了关系型数据库中MySql作为系统的数据库,有效的对数据进行安全的存储,有效的备份,对数据可靠性方面得到了保证。并且程序也具备程序需求的所有功能,使得操作性还是安全性都大大提高,让校园二手书交易平台更能从理念走到现实,确确实实的让人们提升信息处理效率。 关键字:信息管理,时效性,安全性,MySql;Spring Boot
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值