题目链接
http://poj.org/problem?id=3683
2-SAT总结
2-SAT的性质:
以下设x’为x的非
1. 对称性:即若x->y,则一定有y’->x’(原命题和逆否命题等价)
2. x和x’不能同时成立(即x和x’不能在同一个强连通分量之中)
3. 若选择了x,则x的所有后继结点都应被选择, 且x’不可选,则x’的所有前驱结点都不可选
算法
1. 对图跑一遍强连通分量并编号
2. 若x和x’在同一个强连通分量之中,则问题无解
3. 否则,将原题缩点成一个DAG
4. 对DAG求拓扑排序的逆序,若x的拓扑逆序在x’的拓扑逆序之前,则x为真
为什么x的拓扑逆序在x’之前说明x为真
缩点之后,假设X和X’分别在两个不同的强连通分量A和B中,若A->B有一条有向边
由蕴含词->的定义,A->B为真,必然有:
1. A真,B真
2. A假,B假
3. A假,B真
但A和B中分别有x和x‘,则1,2一定不成立,那么必然有A假B真;即:
X为真<=>X所在的强连通分量的拓扑序在X’的强连通之后,则求逆序后就在之前
思路
该题为2-SAT问题,对每对,设x为选择第一个时间出席,则x’为选择第二个时间出席,显然x和x’是不能同时成立的。
然后考虑其余的每对,比如同时在第二个时间举行仪式矛盾,说明有(x’ or y’)<=>(x->y’) or (y->x’)
对每对建立对应的关系即可
代码
#include <iostream>
#include <cstring>
#include <stack>
#include <vector>
#include <set>
#include <map>
#include <cmath>
#include <queue>
#include <sstream>
#include <iomanip>
#include <fstream>
#include <cstdio>
#include <cstdlib>
#include <climits>
#include <deque>
#include <bitset>
#include <algorithm>
using namespace std;
#define PI acos(-1.0)
#define LL long long
#define PII pair<int, int>
#define PLL pair<LL, LL>
#define mp make_pair
#define IN freopen("in.txt", "r", stdin)
#define OUT freopen("out.txt", "wb", stdout)
#define scan(x) scanf("%d", &x)
#define scan2(x, y) scanf("%d%d", &x, &y)
#define scan3(x, y, z) scanf("%d%d%d", &x, &y, &z)
#define sqr(x) (x) * (x)
#define pr(x) cout << #x << " = " << x << endl
#define lc o << 1
#define rc o << 1 | 1
#define pl() cout << endl
const int maxn = 2000 + 10;
vector<int> G[maxn];
int pre[maxn], lowlink[maxn], sccno[maxn], scc_cnt, dfs_clock;
stack<int> S;
struct node {
int x, y, z;
node(int a, int b, int c) : x(a), y(b), z(c) {
}
node() {
}
} a[maxn / 2];
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++;
while (1) {
int x = S.top(); S.pop();
sccno[x] = scc_cnt;
if (x == u) break;
}
}
}
void find_scc(int n) {
dfs_clock = scc_cnt = 0;
memset(sccno, 0, sizeof(sccno));
memset(pre, 0, sizeof(pre));
for (int i = 0; i < n; i++) {
if (!pre[i]) dfs(i);
}
}
int offset;
int NOT(int x) {
return x + offset;
}
bool ss(int i, int j) {
return min(a[i].x + a[i].z, a[j].x + a[j].z) > max(a[i].x, a[j].x);
}
bool tt(int i, int j) {
return min(a[i].y, a[j].y) > max(a[i].y - a[i].z, a[j].y - a[j].z);
}
bool st(int i, int j) {
return min(a[i].x + a[i].z, a[j].y) > max(a[i].x, a[j].y - a[j].z);
}
bool ts(int i, int j) {
return min(a[i].y, a[j].x + a[j].z) > max(a[i].y - a[i].z, a[j].x);
}
void addedge(int sta1, int sta2, int x, int y, int n) {
if (sta1 && sta2) {
G[NOT(x)].push_back(y);
G[NOT(y)].push_back(x);
}
if (!sta1 && !sta2) {
G[x].push_back(NOT(y));
G[y].push_back(NOT(x));
}
if (sta1 && !sta2) {
G[NOT(x)].push_back(NOT(y));
G[y].push_back(x);
}
if (!sta1 && sta2) {
G[x].push_back(y);
G[NOT(y)].push_back(NOT(x));
}
}
vector<int> D[maxn];
int mat[maxn][maxn];
void rebuild(int n) {
for (int i = 0; i <= scc_cnt; i++) D[i].push_back(0);
for (int i = 0; i < n; i++) {
for (int j = 0; j < G[i].size(); j++) {
int v = G[i][j];
if (sccno[i] != sccno[v]) {
if (mat[sccno[v]][sccno[i]]) continue;
mat[sccno[v]][sccno[i]] = 1;
D[sccno[v]].push_back(sccno[i]);
D[sccno[i]][0]++;
}
}
}
}
int tnum[maxn], vis[maxn];
void topsort(int n) {
int tot = 0;
queue<int> Q;
for (int i = 1; i <= n; i++) {
if (D[i][0] == 0) Q.push(i);
}
while (!Q.empty()) {
int x = Q.front(); Q.pop();
vis[x] = 1;
tnum[x] = ++tot;
for (int i = 1; i < D[x].size(); i++) {
int v = D[x][i];
if (vis[v]) continue;
D[v][0]--;
if (D[v][0] == 0) Q.push(v);
}
}
}
int main() {
int n;
scan(n);
offset = n;
for (int i = 0; i < n; i++) {
int h1, h2, m1, m2, t;
scanf("%d:%d %d:%d %d", &h1, &m1, &h2, &m2, &t);
int x = h1 * 60 + m1, y = h2 * 60 + m2;
a[i] = node(x, y, t);
}
for (int i = 0; i < n; i++) {
if (a[i].y - a[i].x < a[i].z) continue;
for (int j = i + 1; j < n; j++) {
if (ss(i, j)) addedge(0, 0, i, j, n);
if (tt(i, j)) addedge(1, 1, i, j, n);
if (st(i, j)) addedge(0, 1, i, j, n);
if (ts(i, j)) addedge(1, 0, i, j, n);
}
}
find_scc(n * 2);
for (int i = 0; i < n; i++) {
if (sccno[i] == sccno[NOT(i)]) {
puts("NO");
return 0;
}
}
puts("YES");
rebuild(n * 2);
topsort(scc_cnt);
for (int i = 0; i < n; i++) {
if (tnum[sccno[i]] < tnum[sccno[NOT(i)]]) printf("%02d:%02d %02d:%02d\n", a[i].x / 60, a[i].x % 60, (a[i].x + a[i].z) / 60, (a[i].x + a[i].z) % 60);
else printf("%02d:%02d %02d:%02d\n", (a[i].y - a[i].z) / 60, (a[i].y - a[i].z) % 60, a[i].y / 60, a[i].y % 60);
}
return 0;
}