2-SAT问题输出可行解
可以把一对夫妇当成一个节点,然后拆点的话,h和w分别为真和假,然后直接按照题目中说的建图染色即可
#include <cstdio>
#include <cstring>
#include <algorithm>
#include <vector>
#include <stack>
#include <queue>
using namespace std;
const int maxn = 222;
int pre[maxn], sccno[maxn], lowlink[maxn], scc_cnt, dfs_clock, ha[maxn];
int col[maxn], ind[maxn];
vector<int> G[maxn], g[maxn];
stack<int> S;
queue<int> que;
void add_clause(int x, int xval, int y, int yval){
x = x * 2 + xval;
y = y * 2 + yval;
G[x ^ 1].push_back(y);
G[y ^ 1].push_back(x);
}
void dfs(int u){
pre[u] = lowlink[u] = ++dfs_clock;
S.push(u);
for (int i = 0; i < G[u].size(); i++){
int v = G[u][i];
if (!pre[v]){
dfs(v);
lowlink[u] = min(lowlink[u], lowlink[v]);
}
else if (!sccno[v])
lowlink[u] = min(lowlink[u], pre[v]);
}
if (lowlink[u] == pre[u]){
scc_cnt += 1;
for (;;){
int x = S.top(); S.pop();
sccno[x] = scc_cnt;
if (x == u) break;
}
}
}
void tarjan(int n){
dfs_clock = scc_cnt = 0;
memset(pre, 0, sizeof(pre));
memset(sccno, 0, sizeof(sccno));
memset(lowlink, 0, sizeof(lowlink));
for (int i = 0; i < n; i++)
if (!pre[i]) dfs(i);
}
int n, m, x, y, a, b;
char c1, c2;
void topsort(){
while(!que.empty()) {
int u = que.front();
que.pop();
int size = g[u].size();
if(!col[u]) col[u] = 1, col[ha[u]] = 2;
for(int i = 0; i < size; i++){
int v = g[u][i];
ind[v]--;
if(ind[v] == 0) que.push(v);
}
}
}
int main(){
while(~scanf("%d%d", &n, &m)){
if (!n && !m) break;
for (int i = 0; i <= n * 2; i++){
G[i].clear(); g[i].clear();
}
for (int i = 0; i < m; i++){
scanf("%d%c %d%c", &x, &c1, &y, &c2);
a = c1 == 'h' ? 0 : 1;
b = c2 == 'h' ? 0 : 1;
add_clause(x, a, y, b);
}
G[0].push_back(1);
tarjan(2 * n);
memset(ha, 0, sizeof(ha));
bool f = 1;
for (int i = 0; i < n; i++)
if (sccno[2 * i] == sccno[2 * i + 1]){
f = 0; break;
}
else ha[sccno[2 * i]] = sccno[2 * i + 1], ha[sccno[2 * i + 1]] = sccno[2 * i];
if (!f){
printf("bad luck\n"); continue;
}
memset(ind, 0, sizeof(ind));
for (int i = 0; i < 2 * n; i++)
for (int j = 0; j < G[i].size(); j++){
int v = G[i][j];
if (sccno[i] != sccno[v]){
g[sccno[v]].push_back(sccno[i]); ind[sccno[i]] += 1;
}
}
while (!que.empty()) que.pop();
for (int i = 1; i <= scc_cnt; i++)
if (ind[i] == 0) que.push(i);
memset(col, 0, sizeof(col));
topsort();
for (int i = 2; i < 2 * n; i += 2){
if (i != 2) printf(" ");
if (col[sccno[i]] == col[sccno[0]]) printf("%dw", i / 2);
else printf("%dh", i / 2);
}
printf("\n");
}
return 0;
}
另附一佳哥版本的解法,速度竟然比一般解法快诶……佳哥不愧把算法当艺术的大牛……
#include <cstdio>
#include <cstring>
#include <algorithm>
#include <vector>
using namespace std;
const int maxn = 222;
struct TowSAT{
vector<int> G[maxn];
bool mark[maxn];
int S[maxn], c, n;
bool dfs(int x){
if (mark[x ^ 1]) return 0;
if (mark[x]) return 1;
mark[x] = 1;
S[c++] = x;
for (int i = 0; i < G[x].size(); i++)
if (!dfs(G[x][i])) return 0;
return 1;
}
void init(int n){
this -> n = n;
for (int i = 0; i <= n; i++)
G[i].clear();
memset(mark, 0, sizeof(mark));
}
void add_clause(int x, int xval, int y, int yval){
x = x * 2 + xval;
y = y * 2 + yval;
G[x ^ 1].push_back(y);
G[y ^ 1].push_back(x);
}
bool solve(){
for (int i = 0; i < n * 2; i += 2)
if (!mark[i] && !mark[i + 1]){
c = 0;
if (!dfs(i)){
while(c > 0) mark[S[--c]] = 0;
if (!dfs(i + 1)) return 0;
}
}
return 1;
}
};
int n, m, x, y, a, b;
char c1, c2;
TowSAT solver;
int main(){
while(~scanf("%d%d", &n, &m)){
if (!n && !m) break;
solver.init(2 * n);
for (int i = 0; i < m; i++){
scanf("%d%c %d%c", &x, &c1, &y, &c2);
a = c1 == 'h' ? 0 : 1;
b = c2 == 'h' ? 0 : 1;
solver.add_clause(x, a, y, b);
}
solver.G[0].push_back(1);
if (!solver.solve()) printf("bad luck");
else for (int i = 2; i < 2 * n; i += 2){
if (i != 2) printf(" ");
if (solver.mark[i] == solver.mark[0]) printf("%dw", i / 2);
else printf("%dh", i / 2);
}
printf("\n");
}
return 0;
}