POJ 3683.Priest John's Busiest Day 2-SAT

Priest John‘s Busiest Day

Time Limit: 2000MS   Memory Limit: 65536K
Total Submissions: 10144   Accepted: 3472   Special Judge

Description

John is the only priest in his town. September 1st is the John‘s busiest day in a year because there is an old legend in the town that the couple who get married on that day will be forever blessed by the God of Love. This year N couples plan to get married on the blessed day. The i-th couple plan to hold their wedding from time Si to time Ti. According to the traditions in the town, there must be a special ceremony on which the couple stand before the priest and accept blessings. The i-th couple need Di minutes to finish this ceremony. Moreover, this ceremony must be either at the beginning or the ending of the wedding (i.e. it must be either from Si to Si + Di, or from Ti - Di to Ti). Could you tell John how to arrange his schedule so that he can present at every special ceremonies of the weddings.

Note that John can not be present at two weddings simultaneously.

Input

The first line contains a integer N ( 1 ≤ N ≤ 1000). 
The next N lines contain the SiTi and DiSi and Ti are in the format of hh:mm.

Output

The first line of output contains "YES" or "NO" indicating whether John can be present at every special ceremony. If it is "YES", output another N lines describing the staring time and finishing time of all the ceremonies.

Sample Input

2
08:00 09:00 30
08:15 09:00 20

Sample Output

YES
08:00 08:30
08:40 09:00

Source

POJ Founder Monthly Contest – 2008.08.31, Dagger and Facer

题目链接:http://poj.org/problem?id=3683

题意:有一天n对夫妻同时结婚,但是只有一个神父,需要神父只需要在结婚典礼开始的d分钟或者结束的d分钟,即[s,s+d]或者[t-d,t]。问能不能举办所有的婚礼。

思路:2-SAT

2-SAT:

  什么是2-SAT呢?就是有一些集合,每个集合中有且仅有两个元素,且不能同时选取两个元素,集合间的元素存在一定的选择关系,求解可行性及可行方案。

算法

1、连边

2、tarjan算法缩点,同一个强联通分量缩成一个点。

3、判可行性,即同一集合中的两个点是否同属一个强连通块

4、缩点建新图,连反边

5、拓扑序,若当前点没有被访问过,则选择该点,不选择其另外的点

(缩点方式改成kosaraju算法(即按照原图拓扑排序的点顺序拓扑排序转置图,最后的强联通分量并且是按照拓扑排序的顺序)可以省略第5步,最后scnno[x]>sccno[¬x]即x为真)

连边

2-SAT算法本身并不难,关键是连边:

a->b即选a必选b。

a、b不能同时选:选了a就要选b‘,选了b就要选a‘。

a、b必须同时选:选了a就要选b,选了b就要选a,选了a‘就要选b‘,选了b‘就要选a‘。

a、b必须选一个:选了a就要选b‘,选了b就要选a‘,选了a‘就要选b,选了b‘就要选a。

※a必须选:a‘->a。

代码:

#include<iostream>
#include<cstdio>
#include<cmath>
#include<cstring>
#include<algorithm>
#include<set>
#include<map>
#include<queue>
#include<stack>
#include<vector>
using namespace std;
typedef long long ll;
typedef pair<int,int> P;
const int maxn=2e3+100,maxm=1e5+100,inf=0x3f3f3f3f,mod=1e9+7;
const ll INF=1e18+7;
priority_queue<P,vector<P>,greater<P> >q;
struct tt
{
    int s,t,d;
} sign[maxn];
vector<int>G[maxn];
void addedge(int u,int v)
{
    G[u].push_back(v);
}
int pre[maxn],lowlink[maxn],sccno[maxn],dfs_clock,scc_cnt;
stack<int>s;
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(true)
        {
            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=1; i<=n; i++)
        if(!pre[i]) dfs(i);
}
tt ans1[maxn],ans2[maxn];
int cmp(tt x,tt y)
{
    return x.t<y.t;
}
int main()
{
    int n;
    scanf("%d",&n);
    for(int i=1; i<=n; i++)
    {
        int st,sm,tt,tm,d;
        scanf("%d:%d %d:%d %d",&st,&sm,&tt,&tm,&d);
        sign[i].s=st*60+sm,sign[i].t=tt*60+tm,sign[i].d=d;
    }
    for(int i=1; i<=n; i++)
    {
        for(int j=1; j<=n; j++)
        {
            if(i==j) continue;
            if(max(sign[i].s,sign[j].s)<min(sign[i].s+sign[i].d,sign[j].s+sign[j].d))
            {
                addedge(i,j+n);
                addedge(j,i+n);
            }
            if(max(sign[i].s,sign[j].t-sign[j].d)<min(sign[i].s+sign[i].d,sign[j].t))
            {
                addedge(i,j);
                addedge(j+n,i+n);
            }
            if(max(sign[i].t-sign[i].d,sign[j].s)<min(sign[i].t,sign[j].s+sign[j].d))
            {
                addedge(i+n,j+n);
                addedge(j,i);
            }
            if(max(sign[i].t-sign[i].d,sign[j].t-sign[j].d)<min(sign[i].t,sign[j].t))
            {
                addedge(i+n,j);
                addedge(j+n,i);
            }
        }
    }
    find_scc(2*n);
    for(int i=1; i<=n; i++)
    {
        if(sccno[i]==sccno[i+n])
        {
            puts("NO");
            return 0;
        }
    }
    puts("YES");
    for(int i=1; i<=n; i++)
    {
        if(sccno[i]>sccno[i+n])
        {
            ans1[i].s=sign[i].s,ans1[i].t=sign[i].s+sign[i].d;
            ans2[i].s=sign[i].t-sign[i].d,ans2[i].t=sign[i].t;

        }
        else
        {
            ans1[i].s=sign[i].t-sign[i].d,ans1[i].t=sign[i].t;
            ans2[i].s=sign[i].s,ans2[i].t=sign[i].s+sign[i].d;
        }
    }
    sort(ans1+1,ans1+n+1,cmp);
    for(int i=1; i<n; i++)
    {
        if(ans1[i].t>ans1[i+1].s)
        {
            for(int j=1; j<=n; j++)
                printf("%02d:%02d %02d:%02d\n",ans2[j].s/60,ans2[j].s%60,ans2[j].t/60,ans2[j].t%60);
            return 0;
        }
    }
    for(int i=1; i<=n; i++)
        printf("%02d:%02d %02d:%02d\n",ans1[i].s/60,ans1[i].s%60,ans1[i].t/60,ans1[i].t%60);
    return 0;
}

2-SAT

POJ 3683.Priest John's Busiest Day 2-SAT

时间: 2024-11-18 09:45:11

POJ 3683.Priest John's Busiest Day 2-SAT的相关文章

POJ 3683 Priest John&#39;s Busiest Day (2-SAT+输出可行解)

题目地址:POJ 3683 第一次做需要输出可行解的题目...大体思路是先用强连通来判断是否有可行解,然后用逆序建图,用拓扑排序来进行染色,然后输出可行解.具体思路见传送门 因为判断的时候少写了一个等号..检查了好长时间..sad... 代码如下: #include <iostream> #include <cstdio> #include <string> #include <cstring> #include <stdlib.h> #incl

POJ 3683(Priest John&#39;s Busiest Day-强连通分量解决2-SAT)[Template:2-SAT]

Priest John's Busiest Day Time Limit: 2000MS   Memory Limit: 65536K Total Submissions: 8144   Accepted: 2769   Special Judge Description John is the only priest in his town. September 1st is the John's busiest day in a year because there is an old le

POJ 3683 Priest John&#39;s Busiest Day(2-SAT)

[题目链接] http://poj.org/problem?id=3683 [题目大意] 每个婚礼有两个时段可以进行特别仪式,特别仪式必须要有神父在场, 神父只有一个,问是否能满足所有婚礼的需求, [题解] 因为两个时段必须要满足一个时段,所以如果一个时段被占用那么另一个时段必须被空出来, 我们根据各个婚礼两个时段之间的冲突关系建边,之后跑2-SAT,判断是否冲突, 若无冲突则输出方案. [代码] #include <cstdio> #include <algorithm> #in

POJ 3683 Priest John&#39;s Busiest Day(2-SAT+方案输出)

Priest John's Busiest Day Time Limit: 2000MS   Memory Limit: 65536K Total Submissions: 10010   Accepted: 3425   Special Judge Description John is the only priest in his town. September 1st is the John's busiest day in a year because there is an old l

POJ 3683 Priest John&#39;s Busiest Day 【2-Sat】

这是一道裸的2-Sat,只要考虑矛盾条件的判断就好了. 矛盾判断: 对于婚礼现场 x 和 y,x 的第一段可以和 y 的第一段或者第二段矛盾,同理,x 的第二段可以和 y 的第一段或者第二段矛盾,条件是 x 的 1或2 段与 y 的 1或2 段有重合,那么选了 x 某一段就不能选择其矛盾的那一段,那就只能选择 y 中的另一段,建立一条 x (u)-> y ( v的对立 ),同理,x (u的对立)<- y ( v ) . 真的,2-sat千万不要用邻接链表!!!卡死卡死卡死!!!稠密图!!!不要

POJ 3684 Priest John&#39;s Busiest Day 2-SAT+输出路径

强连通算法判断是否满足2-sat,然后反向建图,拓扑排序+染色. 一种选择是从 起点开始,另一种是终点-持续时间那个点 开始. 若2个婚礼的某2种时间线段相交,则有矛盾,建边. 容易出错的地方就在于判断线段相交. 若s1<e2&&s2<e1则相交 输出路径的做法可以参考论文2-SAT解法浅析 #include <iostream> #include<cstring> #include<cstdio> #include<string>

HDU2491 Priest John&#39;s Busiest Day

题目链接 题意: 有n个人要进行乒乓球比赛,每个人都一个能力值,每个人出现的次序就是他们住的位置 现在要求进行一场比赛,三个人,裁判的能力值在两个选手之间,住的位置也在两个人的之间 问这种比赛一共可以进行多少次 思路: 用树状数组做,否则TLE,先从左到右扫一遍,计算每点左边大的个数和小的个数, 再从右到左扫一遍,计算每点右边大和小的个数,然后交叉相乘取和就可以了 代码如下: #include<cstdio> #include<cstring> #include<string

UVA - 1420 Priest John&#39;s Busiest Day

题目大意:有一个司仪,要主持 n 场婚礼,给出婚礼的起始时间和终止时间,每个婚礼需要超过一半的时间做为仪式,并且仪式不能终止.问说司仪能否主持 n 场婚礼. 解题思路:贪心,为了尽量主持多的婚礼,每场的仪式时间就一定要尽量短 d = (t - s) / 2 + 1,(因为必须大于一半,所以加 1).然后按照每场婚礼可以最晚结束的时间排序 t - d,(因为要满足所有的婚礼,所以尽量解决早点的仪式,腾出时间来给后面的婚礼),维护一个占用时间值即可. #include <cstdio> #incl

POJ-3683 Priest John&#39;s Busiest Day

图论中的2-SAT.模板题. #include <cstdio> #include <cstdlib> #include <algorithm> #include <cctype> #include <cstring> #include <iostream> using namespace std; #define rep(i, l, r) for(int i=l; i<=r; i++) #define travel(x) fo