算法参考 : 2-SAT 解法浅析 华中师大一附中 赵爽
#include
#include
#include
#include
#include
#include
using namespace std;
const int NN = 100;
const int MM = 1000;
struct TwoSAT {
int n, en, head[NN], next[MM], to[MM], from[MM]; // 原图(前向星), n个点, 2SAT编号0 ~ 2*n-1, x与x^1表示一个点的两个对立sat
bool inS[NN];
stack stk;
int bcnt, vcnt, dfn[NN], low[NN], belong[NN]; // tarjan相关, 各计数/编号都是从0开始
int in[NN], color[NN], op[NN];
vector e[NN]; // 具体求解相关, e为tarjan缩点后建的反图, 用不同的数据结构容易区分~~
void init(int _n) {
// 初始化
n = _n;
en = 0;
for (int i = 0; i < 2 * n; i++) head[i] = -1;
}
void addEdge(int u, int v) {
// 加单向边
from[en] = u; to[en] = v; next[en] = head[u]; head[u] = en++;
}
void tarjan(int u) {
dfn[u] = low[u] = vcnt ++;
stk.push(u); inS[u] = true;
int v;
for (int i = head[u]; i != -1; i = next[i]) {
v = to[i];
if (dfn[v] == -1) {
tarjan(v);
low[u] = min(low[u], low[v]);
} else if (inS[v]) {
low[u] = min(low[u], dfn[v]);
}
}
if (low[u] == dfn[u]) {
bcnt ++;
do {
v = stk.top(); stk.pop();
inS[v] = false;
belong[v] = bcnt - 1;
} while (v != u);
}
}
bool check() {
// 判断2SAT是否有解
vcnt = bcnt = 0;
while (!stk.empty()) stk.pop();
for (int i = 0; i < 2 * n; i++) { inS[i] = false; dfn[i] = -1; }
for (int i = 0; i < 2 * n; i++) if (dfn[i] == -1) tarjan(i);
for (int i = 0; i < 2 * n; i += 2) if (belong[i] == belong[i ^ 1]) return false;
return true;
}
vector solve() {
// 求解2SAT, 返回值为一个长度为n, 元素内容为0或1, 0代表x为解, 1代表x^1为解
for (int i = 0; i < bcnt; i++) {
e[i].clear(); // 如果数据有多组, 点很多, 边很多, 建议从0clear到NN-1为止, 其它vector变量也是如此
in[i] = 0;
}
for (int i = 0; i < 2 * n; i += 2) {
// * 图的边是'对称'的, 因此, 一个浓缩点的对立点应该只有一个
op[belong[i]] = belong[i ^ 1];
op[belong[i ^ 1]] = belong[i];
}
for (int i = 0; i < en; i++) {
// 建缩点后的反图
int u = belong[from[i]];
int v = belong[to[i]];
if (u == v) continue;
e[v].push_back(u);
in[u]++;
}
queue q;
for (int i = 0; i< bcnt; i++) {
color[i] = -1; // 染色法, -1表示没有染色, 1表示红色(选), 0表示蓝色(不选)
if (!in[i]) q.push(i); // 反图拓扑序染色
}
while (!q.empty()) {
int u = q.front(); q.pop();
if (color[u] == -1) { // 如果当前点没有着色
color[u] = 1; // 则染红色
color[op[u]] = 0; // 其对立点染蓝色(及其子孙后继结点也要染成蓝色)
}
for (int i = 0; i<(int)e[u].size(); i++) {
int v = e[u][i];
if (!color[u]) color[v] = 0; // 如果当前点是蓝色, 则其子孙都要染蓝色
if (--in[v] == 0) q.push(v);
}
}
vector ans; // 返回值为vector, 恩, 可能不是很好, 自行更改兼容
for (int i = 1; i < 2 * n; i += 2) { // i = 1开始, 别抄错了
ans.push_back(color[belong[i]]);
}
return ans;
}
} t;
int main() {
int x1, x2, n, m;
char c1, c2;
while (scanf("%d%d", &n, &m), n | m) {
t.init(n);
t.addEdge(0, 1);
for (int i = 1; i <= m; i++) {
scanf("%d%c %d%c", &x1, &c1, &x2, &c2);
x1 <<= 1;
x2 <<= 1;
if (c1 == 'h') x1++;
if (c2 == 'h') x2++;
if (x1 == (x2 ^ 1)) continue;
t.addEdge(x1, x2 ^ 1);
t.addEdge(x2, x1 ^ 1);
}
if (!t.check()) printf("bad luck\n");
else {
vector ans = t.solve();
for (int i = 1; i < n; i++) {
printf("%d%c%c", i, !ans[i] ? 'h' : 'w', i == n - 1 ? '\n' : ' ');
}
}
}
return 0;
}