CodeForces-776D The Door Problem(2-SAT)

题意:有n间房间,一些房间是上锁的,一些是未上锁的,只有所有门未上锁的时候,所有人才能逃出去,这里有m个开关,每个开关都可以控制多扇门,一旦按下一个开关,控制的门的状态都会发生变化,未上锁的变成上锁,上锁的变成未上锁。求是否存在一种情况使得所有的门都未上锁。

分析:题目中给出了暗示,说每个门只被两个开关控制。
我们考虑门的初始状态,如果是未上锁的,我们要保持它的未上锁状态。
我们假定开关按下为1,开关没按下为2。按两次等价于没按。
如果门未上锁,那么按下了第一个开关,第二个开关也要按下,
它的逆否命题也要添加进去,连一条边,第二个开关没按,那么第一个开关也不能按。
如果第一个开关没按,第二个开关也不能按,
它的逆否命题如果第二个开关按了,第一个开关也要按下。
总共四种情况,连4条边。

上锁的状态我们要变成未上锁,这个同理。

然后用tarjan求有向图的强连通分量算法求出是否存在一个开关既要开,又要关的情况。如果存在,输出NO。

#include <iostream>
#include <cstdio>
#include <cstring>
#include <vector>
#include <algorithm>

using namespace std;
const int N = 100005;
int r[N];//门的初始状态
vector<int> a[N];
int h[2 * N], e[8 * N], ne[8 * N], idx;

void add(int a, int b)
{
	e[idx] = b, ne[idx] = h[a], h[a] = idx++;
}

int dfn[2 * N], low[2 * N], timestamp;
int stk[2 * N], in_stk[2 * N], top;
int scc_cnt;
int id[2 * N];
int sz[2 * N];

void tarjan(int u)
{
	dfn[u] = low[u] = ++timestamp;
	stk[++top] = u, in_stk[u] = true;
	for (int i = h[u]; i != -1; i = ne[i])
	{
		int j = e[i];
		if (!dfn[j])
		{
			tarjan(j);
			low[u] = min(low[u], low[j]);
		}
		else if (in_stk[j])
			low[u] = min(low[u], dfn[j]);
	}
	if (dfn[u] == low[u])
	{
		int y;
		++scc_cnt;
		do {
			y = stk[top--];
			in_stk[y] = false;
			id[y] = scc_cnt;
			++sz[scc_cnt];
		} while (y != u);
	}
}

int main()
{
	//rooms switches
	int n, m;
	scanf("%d%d", &n, &m);

	for (int i = 1; i <= n; ++i)
		scanf("%d", &r[i]);//门的状态

	//m个开关
	int d, q;
	for (int i = 1; i <= m; ++i)
	{
		//控制的门数量
		scanf("%d", &d);
		for (int j = 1; j <= d; ++j)
		{
			scanf("%d", &q);
			a[q].push_back(i);//控制门的开关
		}
	}
	
	memset(h, -1, sizeof h);
	for (int i = 1; i <= n; ++i)
	{
		if (r[i])//门未锁
		{
			add(a[i][0] + m, a[i][1] + m);
			add(a[i][1], a[i][0]);
			add(a[i][0], a[i][1]);
			add(a[i][1] + m, a[i][0] + m);
		}
		else//锁了
		{
			add(a[i][0] + m, a[i][1]);
			add(a[i][1] + m, a[i][0]);
			add(a[i][0], a[i][1] + m);
			add(a[i][1], a[i][0] + m);
		}
	}
	
	for (int i = 1; i <= 2 * m; ++i)
	{
		if (!dfn[i])
		{
			tarjan(i);
		}
	}
	
	bool flag = true;
	for (int i = 1; i <= m; ++i)
	{
		if (id[i] == id[i + m])
		{
			flag = false;
		}
	}

	if (flag)
	{
		puts("YES");
	}
	else
	{
		puts("NO");
	}

	return 0;
}
原文地址:https://www.cnblogs.com/pixel-Teee/p/12778946.html