2sat学习笔记

介绍

若干个类似 i or j = 1 的方程组成的方程组,寻找它的可行解的问题,就是2sat问题。方程中未知数个数为n就是n-sat问题。当n大于2时,这个问题是np难问题,因此主要解决的是2-sat问题。

原理

对于i or j = 1这个等式,我们可以得到:

  1. i=0 -> j=1
  2. j=0 -> i=1

这就描述了一种关系。将一个未知量拆成真和假两个状态点,就可以建图了。如果用i代表真,i'代表假,i or j = 1可以表示为:

  1. i' -> j
  2. j' -> i

求解的方法有两种:dfs和tarjan

dfs

选择一个未处理的结点,先选真开始,dfs遍历。如果遍历过程中存在i和i'同时被选到,说明矛盾,换成选假开始。如果还是矛盾,说明无解;否则继续处理剩下结点,直到全部节点都选到,找到一组可行解。

由于图的对称性,可以保证算法正确性(若存在i->j,必有j'->i')。

int head[N];
edge ed[M];
bool mark[N];
stack<int> st;
bool dfs(int p) {
	if(mark[p ^ 1]) return false;
	if(mark[p]) return true;
	mark[p] = true;
	st.push(p);
	for(int e = head[p]; e; e = ed[e].ne) {
		if(!dfs(ed[e].nt)) return false; 
	}
	return true;
}

int main() {
	bool ok = true;
	for(int i = 0; i < 2 * m; i += 2) {
		if(!mark[i] && !mark[i ^ 1]) {
			while(!st.empty()) st.pop();
			if(!dfs(i)) {
				while(!st.empty()) {
					int cur = st.top();
					st.pop();
					mark[cur] = false;
				}
				if(!dfs(i ^ 1)) {
					ok = false;
					break;
				}
			}
		}
	}
	if(ok) cout << "YES" << endl;
	else cout << "NO" << endl;
}
posted @ 2021-03-10 13:02  limil  阅读(69)  评论(0编辑  收藏  举报