题目链接:uva 1466 - String Phone
对于每个位置而言,只有对定角的两个位置可能共存,所有枚举每个位置后可以用dfs预处理出每个地方是哪两个角有可能,然后做2SAT。
#include <cstdio> #include <cstring> #include <cstdlib> #include <vector> #include <algorithm> using namespace std; const int maxn = 3005; const int maxm = 300005; struct TwoSAT { int n, s[maxn * 2], c; bool mark[maxn * 2]; vector<int> g[maxn * 2]; void init(int n) { this->n = n; memset(mark, false, sizeof(mark)); for (int i = 0; i < 2 * n; i++) g[i].clear(); } void addClause(int x, int xflag, int y, int yflag) { x = x * 2 + xflag; y = y * 2 + yflag; g[x^1].push_back(y); g[y^1].push_back(x); } bool dfs (int u) { if (mark[u^1]) return false; if (mark[u]) return true; mark[u] = true; s[c++] = u; for (int i = 0; i < g[u].size(); i++) if (!dfs(g[u][i])) return false; return true; } bool solve() { for (int i = 0; i < 2 * n; i += 2) { if (!mark[i] && !mark[i+1]) { c = 0; if (!dfs(i)) { while (c) mark[s[--c]] = false; if (!dfs(i+1)) return false; } } } return true; } }solver; const int dx[][2] = {{0, 1}, {1, 0}}; const int dy[][2] = {{0, 1}, {0, 1}}; int N, idx[maxn], X[maxn], Y[maxn], F[maxn], C[maxn]; int M, L[maxm], R[maxm], W[maxm]; int E, first[maxn], jump[maxm * 2], link[maxm * 2], val[maxm * 2]; inline int find (int x) { return x == F[x] ? x : F[x] = find(F[x]); } inline int distance(int a, int b) { return abs(X[a] - X[b]) + abs(Y[a] - Y[b]); } inline void addEdge(int u, int v, int w) { jump[E] = first[u]; link[E] = v; val[E] = w; first[u] = E++; } void init () { E = 0; memset(first, -1, sizeof(first)); scanf("%d", &N); for (int i = 0; i < N; i++) { scanf("%d%d", &X[i], &Y[i]); F[i] = i, C[i] = 1; } int u, v; scanf("%d", &M); for (int i = 0; i < M; i++) { scanf("%d%d%d", &u, &v, &W[i]); L[i] = --u; R[i] = --v; if (find(u) != find(v)) { C[find(v)] += C[find(u)]; F[find(u)] = find(v); } addEdge(u, v, W[i]); addEdge(v, u, W[i]); } } bool draw(int u, int c) { if (idx[u] != -1) return idx[u] == c; idx[u] = c; for (int i = first[u]; i != -1; i = jump[i]) { int v = link[i], w = val[i]; if (!draw(v, c ^ ((distance(u, v)&1) ^ (w&1)))) return false; } return true; } bool judge (int u, int k) { memset(idx, -1, sizeof(idx)); if (!draw(u, k)) return false; solver.init(N); for (int i = 0; i < M; i++) { if (find(L[i]) != u || find(R[i]) != u) continue; for (int p = 0; p < 2; p++) { for (int q = 0; q < 2; q++) { int x0 = X[L[i]] + dx[idx[L[i]]][p]; int y0 = Y[L[i]] + dy[idx[L[i]]][p]; int x1 = X[R[i]] + dx[idx[R[i]]][q]; int y1 = Y[R[i]] + dy[idx[R[i]]][q]; if (abs(x0 - x1) + abs(y0 - y1) != W[i]) solver.addClause(L[i], p, R[i], q); } } } return solver.solve(); } int main () { int cas; scanf("%d", &cas); while (cas--) { init(); bool flag = true; for (int i = 0; i < N; i++) { if (i != F[i]) continue; if (!judge(i, 0) && !judge(i, 1)) { flag = false; break; } } printf("%s\n", flag ? "possible" : "impossible"); } return 0; }