【POJ3683】Priest John's Busiest Day (2-sat输出任意解)

36 篇文章 4 订阅


Description 
约翰是街区里唯一的神父。假设有n对新人打算在同一天举行结婚仪式。第i对新人的结婚仪式的时间为Si到Ti,在其仪式开始时或是结束时需要进行一个用时为Di的特别仪式(也就是从Si到Si+Di或是从Ti-Di到Ti),该特别仪式需要神父在场。请判断是否可以通过合理安排每个仪式在开始时或是在结束时进行,从而保证神父能够出席所有的特别仪式。如果可能的话,请输出出席各个特别仪式的时间。当然,神父不可能出席多个仪式,不过神父前往仪式的途中所花费的时间可以忽略不计,神父可以在出席完一个特别仪式后,立即出席另一个开始时间与其结束时间相等的特别仪式 
Input 
第一行为一个整数n表示举行结婚仪式的新人对数,之后n行每行为每个仪式对应的Si,Ti与Di 
Output 
如果存在可行方案,输出YES并输出出席各个特别仪式的时间,否则输出NO 
Sample Input 

08:00 09:00 30 
08:15 09:00 20 
Sample Output 
YES 
08:00 08:30 
08:40 09:00 
Solution 
2-SAT判断可行性,定义变量Xi为在开始时进行第i组新人的特别仪式 
如果Si~Si+Di与Sj~Sj+Dj冲突,则Xi->~Xj,Xj->~Xi 
如果Si~Si+Di与Ti -Di~Ti冲突,则Xi->Xj,~Xj->~Xi 
如果Ti -Di~Ti与Sj~Sj+Dj冲突,则Xj->Xi,~Xi->~Xj 
如果Ti -Di~Ti与Tj -Dj~Tj冲突,则~Xi->Xj,~Xj->Xi 

输出任意解的两种方法都写了写, 判断两个区间是否相交+输出路径

拓扑排序的

#include <iostream>
#include <cstring>
#include <cstdio>
#include <vector>
#include <stack>
#include <algorithm>
using namespace std;
typedef long long ll;
const int maxn = 2e3 + 5;
int n, m, low[maxn], dfn[maxn], id[maxn], scc_cnt, dfs_cnt, cnt[maxn];
int S[maxn], T[maxn], D[maxn], in[maxn], col[maxn], opp[maxn];
vector<int> v[maxn], vt[maxn];
stack<int> s;
void init()
{
    memset(low, 0, sizeof(low));
    memset(id, 0, sizeof(id));
    memset(dfn, 0, sizeof(dfn));
    memset(in, 0, sizeof(in));
    memset(col, -1, sizeof(col));
    memset(opp, 0, sizeof(opp));
    scc_cnt = dfs_cnt = 0;
    for(int i = 0; i < maxn; i++)
        v[i].clear();
    while(!s.empty())
        s.pop();
}
void addedge(int x, int y)
{
    v[x].push_back(y);
}
void addedge2(int x, int y)
{
    vt[x].push_back(y);
}
void tarjan(int x)
{
    dfn[x] = low[x] = ++dfs_cnt;
    s.push(x);
    for(int i = 0; i < v[x].size(); i++)
    {
        int to = v[x][i];
        if(!dfn[to])
        {
            tarjan(to);
            low[x] = min(low[x], low[to]);
        }
        else if(!id[to])
            low[x] = min(low[x], dfn[to]);
    }
    if(low[x] == dfn[x])
    {
        scc_cnt++;
        while(1)
        {
            int u = s.top();
            s.pop();
            id[u] = scc_cnt;
            if(x == u) break;
        }
    }
}
void scc()
{
    for(int i = 0; i < 2*n ; i++)
        if(!dfn[i])
            tarjan(i);
}
int Q[maxn];
void top()
{
    memset(in, 0, sizeof(in));
    memset(col, -1, sizeof(col));
    for(int i = 0; i < 2*n; i++) vt[i].clear();
    for(int i = 0; i < 2*n; i++)
    {
        for(int j = 0; j < v[i].size(); j++)
        {
            int to = v[i][j];
            if(id[i] == id[to]) continue; // 在同一个连通分量不用考虑
            addedge2(id[to], id[i]); //反着存边
            in[id[i]]++; //入度++
        }
    }
    int frnt, rear;
    frnt = rear = 0;
//    cout << scc_cnt << endl;
    for(int i = 1; i <= scc_cnt; i++)
        if(!in[i]) Q[rear++] = i;
    while(frnt != rear)
    {
        int u = Q[frnt++];
        if(col[u] == -1) //未染色
        {
            col[u] = 1;  //染色1
            col[opp[u]] = 0; //对立点染色0
        }
        for(int i = 0; i < vt[u].size(); i++)
        {
            int to = vt[u][i];
            if(--in[to] == 0)
                Q[rear++] = to;
        }
    }
}
int main()
{
    while(~scanf("%d", &n))
    {
        init();
        for(int i = 0; i < n; i++)
        {
            int s1, s2, t1, t2;
            scanf("%d:%d%d:%d%d", &s1, &s2, &t1, &t2, &D[i]);
            S[i] = s1*60 + s2;
            T[i] = t1*60 + t2;
        }
        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])) // 开头两个区间相交
                {
                    addedge(i, j+n); //i 跟 j不能共存, 那么选i就一定选 j+n
                    addedge(j, i+n); //反过来一样
                }
                if(min(S[i]+D[i], T[j]) > max(S[i], T[j]-D[j]))
                {
                    addedge(i, j);  // i 跟 j+n冲突 那么i一定选j
                    addedge(j+n, i+n);
                }
                if(min(S[j]+D[j], T[i]) > max(S[j], T[i]-D[i]))
                {
                    addedge(j, i);
                    addedge(i+n, j+n);
                }
                if(min(T[i], T[j]) > max(T[i]-D[i], T[j]-D[j]))
                {
                    addedge(i+n, j);
                    addedge(j+n, i);
                }
            }
        scc();
        int flag = 0;
        for(int i = 0; i < n; i++)
        {
            if(id[i] == id[i+n])
            {
                flag = 1;
                break;
            }
            opp[id[i]] = id[i+n];
            opp[id[i+n]] = id[i];
        }
        if(flag)
        {
            printf("NO\n");
            continue;
        }
        puts("YES");
        top();
//        for(int i = 0; i < n; i++)
//            cout <<
        for(int i = 0; i < n; i++)
        {
            if(col[id[i]] == 1)
                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);
        }
    }
    return 0;
}

直接scc的

#include <iostream>
#include <cstring>
#include <cstdio>
#include <vector>
#include <stack>
#include <algorithm>
using namespace std;
typedef long long ll;
const int maxn = 2e3 + 5;
int n, m, low[maxn], dfn[maxn], id[maxn], scc_cnt, dfs_cnt, cnt[maxn];
int S[maxn], T[maxn], D[maxn];
vector<int> v[maxn];
stack<int> s;
void init()
{
    memset(low, 0, sizeof(low));
    memset(id, 0, sizeof(id));
    memset(dfn, 0, sizeof(dfn));
    scc_cnt = dfs_cnt = 0;
    for(int i = 0; i < maxn; i++)
        v[i].clear();
    while(!s.empty())
        s.pop();
}
void addedge(int x, int y)
{
    v[x].push_back(y);
}
void tarjan(int x)
{
    dfn[x] = low[x] = ++dfs_cnt;
    s.push(x);
    for(int i = 0; i < v[x].size(); i++)
    {
        int to = v[x][i];
        if(!dfn[to])
        {
            tarjan(to);
            low[x] = min(low[x], low[to]);
        }
        else if(!id[to])
            low[x] = min(low[x], dfn[to]);
    }
    if(low[x] == dfn[x])
    {
        scc_cnt++;
        while(1)
        {
            int u = s.top();
            s.pop();
            id[u] = scc_cnt;
            if(x == u) break;
        }
    }
}
void scc()
{
    for(int i = 0; i < 2*n ; i++)
        if(!dfn[i])
            tarjan(i);
}
int main()
{
    while(~scanf("%d", &n))
    {
        init();
        for(int i = 0; i < n; i++)
        {
            int s1, s2, t1, t2;
            scanf("%d:%d%d:%d%d", &s1, &s2, &t1, &t2, &D[i]);
            S[i] = s1*60 + s2;
            T[i] = t1*60 + t2;
        }
        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])) // 开头两个区间相交
                {
                    addedge(i, j+n); //i 跟 j不能共存, 那么选i就一定选 j+n
                    addedge(j, i+n); //反过来一样
                }
                if(min(S[i]+D[i], T[j]) > max(S[i], T[j]-D[j]))
                {
                    addedge(i, j);  // i 跟 j+n冲突 那么i一定选j
                    addedge(j+n, i+n);
                }
                if(min(S[j]+D[j], T[i]) > max(S[j], T[i]-D[i]))
                {
                    addedge(j, i);
                    addedge(i+n, j+n);
                }
                if(min(T[i], T[j]) > max(T[i]-D[i], T[j]-D[j]))
                {
                    addedge(i+n, j);
                    addedge(j+n, i);
                }
            }
        scc();
        int flag = 0;
        for(int i = 0; i < n; i++)
        {
            if(id[i] == id[i+n])
            {
                flag = 1;
                break;
            }
        }
        if(flag)
        {
            printf("NO\n");
            continue;
        }
        puts("YES");
        for(int i = 0; i < n; i++)
        {
            if(id[i] < id[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);
        }
    }
    return 0;
}





评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值