刷了四天十道2-SAT,基本上知道解决问题的思路了。从这一种题里学会了强连通分量、缩点成树、拓扑排序这么好几个东西,还是有些收获的。
这里留个模板备用,懒得记录解题过程了。。。时间跨度比较大,题目意思都忘了,就那么回事。。。
无非是对于每一对点,看两点之间关系,如果必须同时被选,则加边(a, b), (b, a),不能同时被选(a,~b), (b, ~a);确定某个点必须被选,加(~a, a);某个点必不能选,加(a, ~a),基本上这样就可以将所有的点对以及点自身的关系在有向图上表示出来,用tarjan或者Gabow求强连通分量,如果要输出结果,那么就记录对立点的分量编号,拓排后自底向上(建Dag时反图拓排,按顺序来就行)进行染色,每染一个点将对立分量染成另一种颜色。。。最后按颜色标记答案输出即可。
只做记录用。。。
自己写的模板,各人比较喜欢gabow,在三种强分量算法中码量是最少的,当然也不排除tarjan写的比较好的更短了。。。
class Graph { public: int nv, ne, to[E], nxt[E], head[V]; int dage, toDag[E], nxtDag[E], dag[V]; int idx, scnt, dfn[V], scc[V], oppo[V]; int in[V], col[V]; bool vis[V]; int sta[V], top; int path[V], pp; int q[V], front, rear; Graph(const int &v) :nv(v) { init(); } void init() { ne = idx = scnt = 0; memset(head, -1, sizeof (head[0]) * nv); } void addEdge(const int &u, const int &v) { to[ne] = v, nxt[ne] = head[u], head[u] = ne++; } void addDagEdge(const int &u, const int &v) { toDag[dage] = v, nxtDag[dage] = dag[u], dag[u] = dage++; in[v]++; } void initScc() { top = 0, pp = 0; memset(dfn, -1, sizeof (dfn[0]) * nv); memset(scc, -1, sizeof (scc[0]) * nv); memset(oppo, 0, sizeof (oppo[0]) * nv); } void initDag() { dage = ne; memset(dag, -1, sizeof (dag[0]) * scnt); memset(in, 0, sizeof (in[0]) * scnt); memset(col, 0, sizeof (col[0]) *scnt); } void gabow(int u) { int v; dfn[u] = idx++; sta[top++] = path[pp++] = u; for (int i = head[u]; ~i; i = nxt[i]) if (dfn[v = to[i]] == -1) gabow(v); else if (scc[v] == -1) while (dfn[path[pp - 1]] > dfn[v]) pp--; if (path[pp - 1] == u) { pp--; do { scc[v = sta[--top]] = scnt; } while (u != v); scnt++; } } bool checkScc() { initScc(); for (int i = 0; i < nv; i++) if (dfn[i] == -1) gabow(i); for (int i = 0; i < (nv >> 1); i++) { if (scc[i << 1] == scc[i << 1 | 1]) return false; else { oppo[scc[i << 1]] = scc[i << 1 | 1]; oppo[scc[i << 1 | 1]] = scc[i << 1]; } } return true; } void buildDag() { initDag(); for (int i = 0; i < nv; i++) for (int j = head[i]; ~j; j = nxt[j]) if (scc[i] != scc[to[j]]) addDagEdge(scc[to[j]], scc[i]); } void topoOrder() { buildDag(); for (int i = 0; i < scnt; i++) if (in[i] == 0) q[front = (front + 1) % V] = i; while (rear != front) { int t = q[rear = (rear + 1) % V]; if (!col[t]) col[t] = 1, col[oppo[t]] = -1; for (int i = dag[t]; ~i; i = nxtDag[i]) if (--in[toDag[i]] == 0) q[front = (front + 1) % V] = toDag[i]; } } } *gra;