poj 3207 Ikki's Story IV - Panda's Trick(2-SAT)
题目链接:http://poj.org/problem?id=3207
思路分析:该问题给出N个点,并给出M条连接这些点的线,需要判断是否这些线不会相交;
(1)假设两条线A的端点按照圆圈的顺时针方向依次为A0,A1,同理线B为B0, B1,则可以知道当 A0 < B0 < A1 < B1 或者 B0 < A0 < B1 < A1时线A与B如果同时在内侧或者外侧,则线A与B必定相交;
(2)将M条线视为图中的M个布尔变量,每条线或者在内侧或者在外侧,如果线段A与B在同一侧并定相交,则可以得到等价式:线A在内侧—>线B在外侧,线A在外侧—>线B在内侧,线B在内侧—>线A在外侧,线B在外侧—>线A在内侧;
代码如下:
#include <cstdio> #include <vector> #include <cstring> #include <iostream> using namespace std; const int MAX_N = 5000 + 10; int e[MAX_N][MAX_N]; struct TwoSAT { int n; vector<int> G[2 * MAX_N]; bool mark[2 * MAX_N]; int S[2 * MAX_N], c; void Init(int n) { this->n = n; for (int i = 0; i <= 2 * n; ++i) G[i].clear(); memset(mark, 0, sizeof(mark)); } void AddClause(int x, int y) { int a = 2 * x; int b = 2 * y; G[a].push_back(b ^ 1); G[a ^ 1].push_back(b); G[b].push_back(a ^ 1); G[b ^ 1].push_back(a); } 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 < G[x].size(); ++i) { if (!Dfs(G[x][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 > 0) mark[S[--c]] = false; if (!Dfs(i + 1)) return false; } } } return true; } }; inline bool Judge(int x, int y) { if ((e[x][0] < e[y][0]) && (e[y][0] < e[x][1]) && (e[x][1] < e[y][1])) return true; if ((e[y][0] < e[x][0]) && (e[x][0] < e[y][1]) && (e[y][1] < e[x][1])) return true; return false; } inline void Swap(int &a, int &b) { int temp = a; a = b; b = temp; } TwoSAT sat; int main() { int n, m; while (scanf("%d %d", &n, &m) != EOF) { memset(e, 0, sizeof(e)); sat.Init(m); for (int i = 0; i < m; ++i) { scanf("%d %d", &e[i][0], &e[i][1]); if (e[i][0] > e[i][1]) Swap(e[i][0], e[i][1]); } for (int i = 0; i < m; ++i) { for (int j = i + 1; j < m; ++j) { if (Judge(i, j)) sat.AddClause(i, j); } } bool ok = sat.Solve(); if (ok) printf("panda is telling the truth...\n"); else printf("the evil panda is lying again\n"); } return 0; }