avatar
fireworks99
keep hungry keep foolish

POJ 3683 Paiest John's Busiest Day(2-sat:Tarjan+Topo)

Description

有一个小镇上只有一个牧师。这个小镇上有一个传说,在九月一日结婚的人会受到爱神的保佑,但是要牧师举办一个仪式。这个仪式要么在婚礼刚刚开始的时候举行,要么举行完婚礼正好结束。
现在已知有n场婚礼,告诉你每一场的开始和结束时间,以及举行仪式所需要的时间。问牧师能否参加所有的婚礼,如果能则输出一种方案.

2-sat

①输入+初始化 input() + initial()

②添边 add_edge()

③缩点 Tarjan()

④判可行否 judge()

⑤若可行,记下超点的对立点

⑥反向建边拓扑染色:

遍历所有的点

对于每个点:

若与他相连接的点跟它本身不处于同一个强连通分量中

将两个强连通分量(即两个超点)反向建边

进行BFS拓扑染色

Code

#include <stack>
#include <queue>
#include <vector>
#include <cstdio>
#include <cstring>
#include <iostream>
#include <algorithm>
using namespace std;
const int N = 10005;

struct time
{
    int start, over;
} tm[N];

stack<int> st;
vector<int> v[N];///Trajan用
vector<int> e[N];///Topo用

///弄明白t跟num!
int n, m, t, num;///num:强连通分量(连通块)的个数,缩点后点的个数

bool vis[N];///该点是否访问过
int inst[N];///该点是否在栈中

int time[N];///i点第一次被访问的时间
int low[N];///i点所在强连通分量子图中第一个被搜到的点的time值

int ltt[N];///i点属于第几个强连通分量(0表示不属于任何一个)
int OPP[N];///超点的对立点

int in[N];
int color[N];///拓扑染色,下标是超点!

void Tarjan(int x)///搜索x点及其子树
{
    int temp;
    st.push(x);
    inst[x] = vis[x] = 1;
    low[x] = time[x] = ++t;
    for(int i = 0; i < v[x].size(); i++)
    {
        temp = v[x][i];
        if(vis[temp] == 0)
        {
            Tarjan(temp);
            low[x] = min(low[x], low[temp]);
        }
        else if(inst[temp] == 1)///成环时
            low[x] = min(low[x], time[temp]);
        else///点temp访问过却不在当前栈里:属于已发现的强连通分量
            continue;///这两个不能合二为一的原因:只有从此指彼的单向路
    }
    if(low[x] == time[x])
    {
        num++;///找到第num个强连通分量
        while(!st.empty())
        {
            temp = st.top();
            st.pop();
            ltt[temp] = num;///temp结点属于第num个强连通分量
            inst[temp] = 0;
            if(temp == x)
                break;
        }
    }
}

bool Error(int a, int b)
{
    if(tm[a].start >= tm[b].over || tm[b].start >= tm[a].over)
        return 0;
    return 1;
}

void Topo()
{
    memset(in, 0, sizeof(in));
    memset(color, -1, sizeof(color));

    ///遍历所有的点
    ///对于每个点:
    ///若与他相连接的点跟它本身不处于同一个强连通分量中
    ///将两个强连通分量(即两个超点)反向建边
    ///进行BFS拓扑染色
    for(int i = 1; i <= 2 * n; ++i)
        for(int j = 0; j < v[i].size(); ++j)///多个vector注意区分清楚
        {
            int tem = v[i][j];
            if(ltt[i] == ltt[tem])
                continue;
            e[ ltt[tem] ].push_back(ltt[i]);
            in[ ltt[i] ]++;
        }

    queue<int> q;
    for(int i = 1; i <= num; ++i)
        if(!in[i])
            q.push(i);

    while(!q.empty())
    {
        int tem = q.front();
        q.pop();
        if(color[tem] == -1)
        {
            color[tem] = 1;
            color[ OPP[tem] ] = 0;
        }
        for(int i = 0; i < e[tem].size(); ++i)
        {
            in[ e[tem][i] ]--;
            if(in[ e[tem][i] ] == 0)
                q.push(e[tem][i]);
        }
    }
}

void init()///只一组数据无需init()
{
    t = num = 0;
    memset(vis, 0, sizeof(vis));
    memset(inst, 0, sizeof(inst));
    memset(time, 0, sizeof(time));
    memset(low, 0, sizeof(low));
    memset(ltt, 0, sizeof(ltt));
    memset(color, -1, sizeof(color));
}

///强连通分量内元素个数最少为1(该元素本身)
///这意味着每个元素都有从属的强连通分量

int main()
{
    scanf("%d", &n);

    ///①init();
    ///②input();
    int ssh, ssm, eeh, eem, len;
    for(int i = 1; i <= n; ++i)
    {
        scanf("%d:%d %d:%d %d", &ssh, &ssm, &eeh, &eem, &len);
        tm[i].start = ssh * 60 + ssm;
        tm[i].over = tm[i].start + len;
        tm[i + n].over = eeh * 60 + eem;
        tm[i + n].start = tm[i + n].over - len;
    }

    ///③add_edge();
    ///注意此题跟 POJ 3207 的不同
    ///那题一冲突都冲突,这题不一样
    int addnum = 0;
    for(int i = 1; i <= n; ++i)
        for(int j = 1; j <= n; ++j)///跟之前那题不一样,必须这么写!!!
        {
            if(i == j)
                continue;
            if(Error(i, j))
                v[i].push_back(j + n);
            if(Error(i, j + n))
                v[i].push_back(j);
            if(Error(i + n, j))
                v[i + n].push_back(j + n);
            if(Error(i + n, j + n))
                v[i + n].push_back(j);
        }

    ///④Tarjan();
    for(int i = 1; i <= 2 * n; ++i)
        if(!vis[i])
            Tarjan(i);

    ///⑤judge();
    for(int i = 1; i <= n; ++i)
    {
        if(ltt[i] == ltt[i + n])
        {
            cout << "NO" << '\n';
            return 0;
        }
        OPP[ ltt[i] ] = ltt[i + n];///之前傻地写成OPP[ ltt[i] ] = OPP[ ltt[i + n] ];
        OPP[ ltt[i + n] ] = ltt[i];
    }
    cout << "YES" << '\n';

    ///⑥Topo();
    Topo();

    for(int i = 1; i <= n; ++i)
    {
        if(color[ ltt[i] ])///color 里下标是超点
            printf("%.2d:%.2d %.2d:%.2d\n", tm[i].start / 60, tm[i].start % 60, tm[i].over / 60, tm[i].over % 60);
        else
            printf("%.2d:%.2d %.2d:%.2d\n", tm[i+n].start / 60, tm[i+n].start % 60, tm[i+n].over / 60, tm[i+n].over % 60);
    }
    return 0;
}

Kosaraju算法

#include <vector>
#include <cstdio>
#include <cstring>
#include <iostream>
#include <algorithm>
using namespace std;
const int MAX_V = 10005;

int n;
vector<int> vs;
vector<int> G[MAX_V];
vector<int> rG[MAX_V];
vector<int> nv[MAX_V];

struct time
{
    int start, over;
} tm[MAX_V];


bool used[MAX_V];
int cmp[MAX_V];

void add_eage(int from, int to)
{
    G[from].push_back(to);
    rG[to].push_back(from);
}

void DFS(int v)
{
    used[v] = 1;
    for(int i = 0; i < G[v].size(); ++i)
        if(!used[ G[v][i] ])
            DFS(G[v][i]);
        vs.push_back(v);
}

void rDFS(int v, int k)
{
    used[v] = 1;
    cmp[v] = k;
    for(int i = 0; i < rG[v].size(); ++i)
        if(!used[ rG[v][i] ])
            rDFS(rG[v][i], k);
}

int scc()
{
    memset(used, 0, sizeof(used));
    vs.clear();
    for(int v = 1; v <= n; ++v)
        if(!used[v])
            DFS(v);
    memset(used, 0, sizeof(used));
    int k = 0;
    for(int i = vs.size() - 1; i >= 0; --i)
        if(!used[vs[i]])
            rDFS(vs[i], ++k);
    return k;
}

bool Error(int a, int b)
{
    if(tm[a].start >= tm[b].over || tm[b].start >= tm[a].over)
        return 0;
    return 1;
}

int main()
{
    scanf("%d", &n);
    int ssh, ssm, eeh, eem, len;
    for(int i = 1; i <= n; ++i)
    {
        scanf("%d:%d %d:%d %d", &ssh, &ssm, &eeh, &eem, &len);
        tm[i].start = ssh * 60 + ssm;
        tm[i].over = tm[i].start + len;
        tm[i + n].over = eeh * 60 + eem;
        tm[i + n].start = tm[i + n].over - len;
    }

    for(int i = 1; i <= n; ++i)
        for(int j = 1; j <= n; ++j)///跟之前那题不一样,必须这么写!!!
        {
            if(i == j)
                continue;
            if(Error(i, j))
                add_eage(i, j + n);
            if(Error(i, j + n))
                add_eage(i, j);
            if(Error(i + n, j))
                add_eage(i + n, j + n);
            if(Error(i + n, j + n))
                add_eage(i + n, j);
        }

    int num = scc();
    for(int i = 1; i <= n; i++)
        if(cmp[i] == cmp[i + n])
        {
            puts("NO");
            return 0;
        }

    puts("YES");
    for(int i = 1; i <= n; i++)
    {
        if(cmp[i] > cmp[i + n]) // 在仪式开始时举行
            printf("%.2d:%.2d %.2d:%.2d\n", tm[i].start / 60, tm[i].start % 60, tm[i].over / 60, tm[i].over % 60);
        else
            printf("%.2d:%.2d %.2d:%.2d\n", tm[i+n].start / 60, tm[i+n].start % 60, tm[i+n].over / 60, tm[i+n].over % 60);
    }
    return 0;
}
Site by Baole Zhao | Powered by Hexo | theme PreciousJoy