2sat学习笔记
介绍
若干个类似 i or j = 1 的方程组成的方程组,寻找它的可行解的问题,就是2sat问题。方程中未知数个数为n就是n-sat问题。当n大于2时,这个问题是np难问题,因此主要解决的是2-sat问题。
原理
对于i or j = 1这个等式,我们可以得到:
- i=0 -> j=1
- j=0 -> i=1
这就描述了一种关系。将一个未知量拆成真和假两个状态点,就可以建图了。如果用i代表真,i'代表假,i or j = 1可以表示为:
- i' -> j
- 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;
}