POJ-3683 Priest John's Busiest Day (2-SAT 求任意可行方案)

题目链接:POJ-3683 Priest John's Busiest Day

题意

$n$对新人要举行婚礼,每个婚礼有各自的开始时间$S_i$和结束时间$T_i$,还要举行一个时长为$D_i$的仪式,这个仪式只能在$S_i$或$T_i-D_i$时刻开始,问如何安排每个婚礼举行仪式的时间,让一个神父能够主持每个婚礼的仪式。


思路

把每个婚礼看成一个集合,两个可以举行仪式的时间段是集合里的两个元素。不同集合的时间段可能会有冲突,我们要从每个集合里选出一个时间段,令选出的$n$个时间段两两不冲突,这就是一个2-SAT求任意可行方案问题。

2-SAT入门:传送门


代码实现

#include <iostream>
#include <cstdio>
#include <cstring>
using std::min;
using std::max;
const int N = 2010, M = N * N;
struct Edge
{
    int to, nex;
} edge[M];
int num_e;
int head[N];
int dfn[N], low[N], scc[N], sz[N], idx, tot;
bool insta[N];
int sta[N], top;
int S[N], T[N], D[N];
void init() {
    num_e = 0, top = 0, idx = 0, tot = 0;
    memset(head, 0, sizeof(head));
    memset(insta, 0, sizeof(insta));
    memset(scc, 0, sizeof(scc));
    memset(dfn, 0, sizeof(dfn));
}
void add_edge(int x, int y) {
    edge[++num_e].to = y;
    edge[num_e].nex = head[x];
    head[x] = num_e;
}
void tarjan(int u) {
    dfn[u] = low[u] = ++idx;
    sta[++top] = u;
    insta[u] = true;
    for (int i = head[u]; i; i = edge[i].nex) {
        int v = edge[i].to;
        if (!dfn[v]) {
            tarjan(v);
            low[u] = min(low[u], low[v]);
        }
        else if (insta[v]) low[u] = min(low[u], dfn[v]);
    }
    if (dfn[u] == low[u]) {
        ++tot;
        do {
            scc[sta[top]] = tot;
            sz[tot]++;
            insta[sta[top]] = false;
        } while (sta[top--] != u);
    }
}
void solve(int n) {
    for (int i = 0; i < n; i++) {
        for (int j = i + 1; j < n; j++) {
            if (min(S[i] + D[i], S[j] + D[j]) > max(S[i], S[j])) {
                add_edge(i, j + n);
                add_edge(j, i + n);
            }
            if (min(S[i] + D[i], T[j]) > max(S[i], T[j] - D[j])) {
                add_edge(i, j);
                add_edge(j + n, i + n);
            }
            if (min(T[i], S[j] + D[j]) > max(T[i] - D[i], S[j])) {
                add_edge(i + n, j + n);
                add_edge(j, i);
            }
            if (min(T[i], T[j]) > max(T[i] - D[i], T[j] - D[j])) {
                add_edge(i + n, j);
                add_edge(j + n, i);
            }
        }
    }
    for (int i = 0; i < 2 * n; i++) {
        if (!dfn[i]) tarjan(i);
    }
    for (int i = 0; i < n; i++) {
        if (scc[i] == scc[i+n]) {
            puts("NO");
            return ;
        }
    }
    puts("YES");
    for (int i = 0; i < n; i++) {
        if (scc[i] < scc[i+n]) printf("%02d:%02d %02d:%02d\n", S[i] / 60, S[i] % 60, (S[i] + D[i]) / 60, (S[i] + D[i]) % 60);
        else printf("%02d:%02d %02d:%02d\n", (T[i] - D[i]) / 60, (T[i] - D[i]) % 60, T[i] / 60, T[i] % 60);
    }
}

int main() {
    int n;
    while (~scanf("%d", &n)) {
        init();
        for (int i = 0, h1, h2, m1, m2; i < n; i++) {
            scanf("%02d:%02d %02d:%02d %d", &h1, &m1, &h2, &m2, &D[i]);
            S[i] = h1 * 60 + m1;
            T[i] = h2 * 60 + m2;
        }
        solve(n);
    }
    return 0;
}
View Code

 

posted @ 2019-08-17 20:37  _kangkang  阅读(256)  评论(1编辑  收藏  举报