POJ3683-Priest John's Busiest Day(2-SAT)

1 篇文章 0 订阅

题目链接

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;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值