• 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
    ", S[i] / 60, S[i] % 60, (S[i] + D[i]) / 60, (S[i] + D[i]) % 60);
            else printf("%02d:%02d %02d:%02d
    ", (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
    作者:_kangkang
    本文版权归作者和博客园共有,欢迎转载,但必须给出原文链接,并保留此段声明,否则保留追究法律责任的权利。
  • 相关阅读:
    前端资料
    贪心
    二叉树的最大深度
    最长回文子串
    动态规划-tsp
    动态规划
    spfa与SLF和LLL(复习)
    动态规划之最长 公共子序列和上升子序列
    最近最远距离之暴力优化
    基于Element-UI封装的季度插件
  • 原文地址:https://www.cnblogs.com/kangkang-/p/11370254.html
Copyright © 2020-2023  润新知