题意:
题目链接:http://123.57.161.63:8001/problem/12/
给出一个无向图,每次选择一个点u,将与u相连的边都删去,然后在原先互不相连的u和v之间加上边(u,v)。问最后是否可以构成一个完全图。
思路:
可以发现每个点最多只会被选择一次,因为选择两次和不选没有区别。
如果将图表示成邻接矩阵,每次选择点x,其实就是将第x行和第x列01反转。那对于矩阵的上的每个点(x,y)受到选择第x个和选择第y个的影响。
这样就都等于要确定n个布尔变量的取值,使得满足n^2个条件,典型的2-SAT问题。
代码:
#include <bits/stdc++.h>
using namespace std;
const int MAXN = 2005;
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 ^ 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]] = false;
if (!dfs(i + 1)) return false;
}
}
}
return true;
}
} twoSAT;
bool mp[MAXN][MAXN];
int main() {
//freopen("in.txt", "r", stdin);
int T, cs = 0;
scanf("%d", &T);
while (T--) {
int n, m;
memset(mp, false, sizeof(mp));
scanf("%d%d", &n, &m);
for (int i = 1; i <= m; i++) {
int u, v;
scanf("%d%d", &u, &v);
mp[u][v] = mp[v][u] = true;
}
twoSAT.init(n);
for (int i = 1; i <= n; i++) {
for (int j = i + 1; j <= n; j++) {
if (mp[i][j]) {
twoSAT.add_clause(i - 1, 1, j - 1, 0);
twoSAT.add_clause(i - 1, 0, j - 1, 1);
}
else {
twoSAT.add_clause(i - 1, 1, j - 1, 1);
twoSAT.add_clause(i - 1, 0, j - 1, 0);
}
}
}
printf("Case %d: ", ++cs);
if (twoSAT.solve()) puts("YES");
else puts("NO");
}
return 0;
}