2-SAT模板

n-SAT 是给定多个条件,问是否有一个赋值方式使所有条件得到满足。

每个条件都有n个变量, 而2-SAT 是存在时间复杂度为\(O(nm)或O(n+m)\)的做法(\(m\)是条件数)的算法的问题。

2-SAT往往是判断是否有方案使条件得到满足的一个算法。

建图:
求解2-SAT问题,需要转移到图上,对于每个变量都定义两个点分别表示true和false,用点a到点b连边表示a要选b也一要选。因此a的拓扑序一定比b要大,因为b给了a一个限制。

判断:

2-SAT有多种实现方式,用的最多的就是用tarjan来判断可行性。

tarjan来判断可行性的原理是,通过缩点之后,如果每个变量的true和false不属于一个连通分量,就说明可行。

输出方案:

因为缩点之后是深搜使得tarjan缩完点之后的编号是是倒着的拓扑序,而我们又想找到对后来限制少的点,即拓扑序大的点,所以我们找tarjan缩完点之后的编号小的点。

#include <bits/stdc++.h>
#include <stack>
#define N 2001010
using namespace std;
struct edge {
	int to, nex;
}e[N * 2];
int n, m, tot, color, cnt, flag[N], lin[N], dfn[N], low[N], belong[N], vis[N];
stack <int> s;
inline void add(int f, int t)
{
	e[++cnt].to = t;
	e[cnt].nex = lin[f];
	lin[f] = cnt;
}
void tarjan(int now)
{
 	dfn[now] = low[now] = ++tot;
 	vis[now] = 1, s.push(now);
 	for (int i = lin[now]; i; i = e[i].nex)
 	{
 		int to = e[i].to;
 		if (!dfn[to])
 			tarjan(to), low[now] = min(low[now], low[to]);
 		else if (vis[to])
 			low[now] = min(low[now], dfn[to]);
 	}
 	if (dfn[now] == low[now])
 	{
 		++color;
 		while (!s.empty())
 		{
 			int v = s.top(); s.pop();
 			belong[v] = color, vis[v] = 0;
 			if (v == now) break;
 		}
 	}
}
int main()
{
 	scanf("%d%d", &n, &m);
 	while (m--)
 	{
 		int i, a, j, b;		
 		scanf("%d%d%d%d", &i, &a, &j, &b);
 		add(j + (b ^ 1) * n, i + a * n);
  		add(i + (a ^ 1) * n, j + b * n);
 	}//就是xi为a时, xj一定 不为b,反过来xj一定不为b时,xi并不满足一定为a,因此要从j+(b^1)*n连向i+a*n 
  	for (int i = 1; i <= n * 2; i++)
  		if (!dfn[i])     	
 		 	tarjan(i);   	
  	for (int i = 1; i <= n; i++)
 		if (belong[i] == belong[i + n])	
 			printf("IMPOSSIBLE\n"), exit(0);
 	printf("POSSIBLE\n");	//最后我们要选对后来限制小的点,所以就是要选拓扑序大的,也就是belong小的 
 	for (int i = 1; i <= n; i++)
 	{						
 		if (belong[i] < belong[i + n])//无相等情况 
 			printf("1 ");	
 		else				
 			printf("0 ");	
 	}						
 	return 0;				
}
posted @ 2019-10-09 10:51  DAGGGGGGGGGGGG  阅读(228)  评论(0编辑  收藏  举报