poj3683

poj3683

题意

n对新人举行婚礼,婚礼在不同时间段但可能重叠,婚礼有开始(Si)、结束(Ti)、仪式举行时间(Di),问能否给出一种举行方案,使得神父能参加所有的婚礼并举行仪式。

分析

xi为真 <=> 在开始时举行仪式,
xj为假 <=> 在结束时举行仪式。
设x' 为 非x。(若 x 为真,则 x' 为假)
xi -> xj 表示 xi 为真则 xj 为真。(xi xj 连边)

对于结婚仪式 i 和 j ,如果 Si ~ Si+Di 和 Sj ~ Sj+Dj 冲突,就有 xi' or xj' ,这种情况即 xi -> xj' ,xj -> xi' 。(xi 和 xj 不能同时选,即选择一个,那么选了 xi 后 就会选择到 xj' ,表示不选 xj。)
如果 Si ~ Si+Di 和 Tj-Dj ~ Dj 冲突,有 xi' or xj ,即 xi -> xj,xj' -> xi' 。
如果 Ti-Di ~ Ti 和 Sj ~ Sj+Dj 冲突,有 xi or xj' ,即 xi' -> xj',xj -> xi 。
如果 Ti-Di ~ Ti 和 Tj-Dj ~ Dj 冲突 ,有 xi or xj ,即 xi' -> xj ,xj' -> xi 。

代码参照《 挑战程序设计竞赛》,网上的代码大多是按照论文进行模拟?

书中有个步骤比较疑惑,打印可行解时,

x 所在的强连通分量的拓扑序在 x' 所在的强连通分量之后 <=> x 为真。

一点理解:拓扑序靠后的出度小,越不会影响后面的选择?

code

#include<iostream>
#include<cstdio>
#include<cstring>
#include<cmath>
#include<queue>
#include<stack>
#include<set>
#include<map>
#include<algorithm>
typedef long long ll;

using namespace std;

const int MAXN = 2e3 + 10;
int n, m; // 点、边
int vis[MAXN];
int flag[MAXN]; // 所属强连通分量的拓扑序
vector<int> G[MAXN], rG[MAXN];
vector<int> vs; // 后序遍历顺序的顶点列表
void addedge(int x, int y)
{
    G[x].push_back(y);  // 正向图
    rG[y].push_back(x); // 反向图
}
void dfs(int u)
{
    vis[u] = 1;
    for(int i = 0; i < G[u].size(); i++)
    {
        int v = G[u][i];
        if(!vis[v]) dfs(v);
    }
    vs.push_back(u);
}
void rdfs(int u, int k)
{
    vis[u] = 1;
    flag[u] = k;
    for(int i = 0; i < rG[u].size(); i++)
    {
        int v = rG[u][i];
        if(!vis[v]) rdfs(v, k);
    }
}
int scc() // 强连通分量的个数
{
    vs.clear();
    memset(vis, 0, sizeof vis);
    for(int i = 1; i <= n; i++) //  [1...n]
        if(!vis[i]) dfs(i);
    memset(vis, 0, sizeof vis);
    int k = 0;
    for(int i = vs.size() - 1; i >= 0; i--)
        if(!vis[vs[i]]) rdfs(vs[i], k++);
    return k;
}

int S[MAXN], D[MAXN], T[MAXN];

void solve()
{
    int N = n;
    n = 2 * n;
    for(int i = 1; i <= n; i++)
    {
        G[i].clear();
        rG[i].clear();
    }
    for(int i = 1; i <= n; i++) // 枚举、连边
    {
        for(int j = 1; j < i; j++)
        {
            if(min(S[i] + D[i], S[j] + D[j]) > max(S[i], S[j]))
            {
                // xi -> xj' ,xj -> xi'
                addedge(i, j + N);
                addedge(j, i + N);
            }
            if(min(S[i] + D[i], T[j]) > max(S[i], T[j] - D[j]))
            {
                // xi -> xj,xj' -> xi'
                addedge(i, j);
                addedge(j + N, i + N);
            }
            if(min(T[i], S[j] + D[j]) > max(S[j], T[i] - D[i]))
            {
                //  xi' -> xj',xj -> xi
                addedge(i + N, j + N);
                addedge(j, i);
            }
            if(min(T[i], T[j]) > max(T[i] - D[i], T[j] - D[j]))
            {
                // xi' -> xj ,xj' -> xi
                addedge(i + N, j);
                addedge(j + N, i);
            }
        }
    }
    scc();

    // 判断可行性
    for(int i = 1; i <= N; i++)
    {
        if(flag[i] == flag[N + i])
        {
            puts("NO");
            return;
        }
    }

    // 输出可行解
    puts("YES");
    for(int i = 1; i <= N; i++)
    {
        if(flag[i] > flag[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()
{
    while(~scanf("%d", &n))
    {
        for(int i = 1; i <= n; i++)
        {
            int x1, y1, x2, y2;
            char p, q;
            scanf("%02d:%02d %02d:%02d %d", &x1, &y1, &x2, &y2, &D[i]);
            S[i] = x1 * 60 + y1;
            T[i] = x2 * 60 + y2;
        }
        solve();
    }
    return 0;
}
posted @ 2017-05-01 09:52  ftae  阅读(554)  评论(0编辑  收藏  举报