HDU 1814 Peaceful Commission[2_SAT]
2—sat建图总结
1.元素关系有以下11种 A[x] NOT A[x] A[x] AND A[y] A[x] AND NOT A[y] A[x] OR A[y] A[x] OR NOT A[y] NOT (A[x] AND A[y]) NOT (A[x] OR A[y]) A[x] XOR A[y] NOT (A[x] XOR A[y]) A[x] XOR NOT A[y] And 结果为1:建边 ~x->y, ~y->x (两个数都为1) And 结果为0:建边 y->~x , x->~y(两个数至少有一个为0) OR 结果为1:建边 ~x->y , ~y->x(两个数至少有一个为1) OR 结果为0:建边 x->~x , y->~y(两个数都为0) XOR 结果为1:建边 x->~y , ~x->y , ~y->x , y -> ~x (两个数一个为0,一个为1) XOR 结果为0:建边 x->y , ~x->~y , y->x ~y->~x(两个数同为1或者同为0)
题意:找出字典序最小的解
分析:直接暴力枚举DFS,首先将所有的点都置为为染色,然后我们从第一个点开始DFS染色,我们先尝试将i染
成红色(答案中的颜色),将~i染成蓝色,然后dfs i的所有后继并染色,如果对于后继j没有染色,那么将j然
后为红色,~j染成蓝色。如果后继j已经被染成蓝色,则说明不能选则i,如果j已经染成红色,则说明可以。
那么这些后继就可以被选择。如果选择i的时候失败了,那么必定要选择~i,如果也失败,则说明无解。否则
按次序选取下一个未被染色的点。时间复杂度O(nm)。
#include <stdio.h> #include <string.h> #include <stdlib.h> #include <vector> using namespace std; #define clr(x)memset(x,0,sizeof(x)) #define R 1 #define B 2 #define W 0 const int maxn = 16005; struct node { int to, next; }e[100000]; int head[maxn]; int tot; void add(int s, int t) { e[tot].to = t; e[tot].next = head[s]; head[s] = tot++; } int cnt; int col[maxn]; int ans[maxn]; int n, m; bool dfs(int u) { if (col[u] == B) return false; if (col[u] == R) return true; col[u] = R; col[u^1] = B; ans[cnt++] = u; int i; int k; for (i=head[u]; i; i=e[i].next) { k = e[i].to; if (!dfs(k)) return false; } return true; } bool solve() { int i, j; memset(col,0,sizeof(col)); for (i=0; i<n; i++){ if (col[i]) continue; cnt = 0; if (!dfs(i)) { for (j=0; j<cnt; j++) { col[ans[j]] = W; col[ans[j]^1] = W; } if (!dfs(i^1)) return false; } } return true; } int main() { while (scanf("%d %d",&n, &m)!=EOF) { n <<= 1; int i; tot = 1; clr(head); int a, b; while (m--) { scanf("%d %d",&a, &b); a--; b--; add(a, b^1); add(b, a^1); } if (solve()) { for (i=0; i<n; i++) if(col[i] == R) printf("%d\n",i+1); } else printf("NIE\n"); } return 0; }