题意:
题目链接:http://acm.split.hdu.edu.cn/showproblem.php?pid=4421
按照题目所示的转移方式,问是否存在一个a数组可以转译成b矩阵。
思路:
因为都是位运算,b[x][y]只会由两个bool变量影响,其实只要将每个数字按位拆开来进行2-SAT即可。
#include <bits/stdc++.h>
using namespace std;
const int MAXN = 505;
struct TwoSAT {
int n;
vector <int> G[MAXN * 2];
bool mark[MAXN * 2];
int S[MAXN * 2], c;
bool dfs(int x) {
if (mark[x ^ 1]) return false;
if (mark[x]) return true;
mark[x] = true;
S[c++] = x;
for (int i = 0; i < (int)G[x].size(); i++)
if (!dfs(G[x][i])) return false;
return true;
}
void init(int n) {
this -> n = n;
for (int i = 0; i < n * 2; 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].push_back(y);
}
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]] = false;
if (!dfs(i + 1)) return false;
}
}
}
return true;
}
} twoSAT;
int a[MAXN][MAXN];
int main() {
//freopen("in.txt", "r", stdin);
int n;
while (scanf("%d", &n) != EOF) {
for (int i = 0; i < n; i++) {
for (int j = 0; j < n; j++)
scanf("%d", &a[i][j]);
}
bool flag = true;
for (int k = 0; k < 32; k++) {
twoSAT.init(n);
for (int i = 0; i < n; i++) {
for (int j = 0; j < n; j++) {
int t = a[i][j] & (1 << k);
if (i == j) {
if (a[i][j] != 0) {
flag = false;
break;
}
continue;
}
if (i % 2 == 0 && j % 2 == 0) {
if (t) twoSAT.add_clause(i, 1, j, 1);
else twoSAT.add_clause(i, 1, j, 0);
}
else if (i % 2 == 1 && j % 2 == 1) {
if (t) twoSAT.add_clause(i, 0, j, 1);
else twoSAT.add_clause(i, 0, j, 0);
}
else {
if (t) {
twoSAT.add_clause(i, 1, j, 0);
twoSAT.add_clause(i, 0, j, 1);
}
else {
twoSAT.add_clause(i, 1, j, 1);
twoSAT.add_clause(i, 0, j, 0);
}
}
}
if (!flag) break;
}
if (!flag) break;
if (!twoSAT.solve()) {
flag = false;
break;
}
}
if (flag) puts("YES");
else puts("NO");
}
return 0;
}