POJ 3683 2-SAT 输出可行方案

http://poj.org/problem?id=3683


1,根据矛盾建边

2,tarjan求scc判断可行性

3,根据scc缩点建反向边

4,拓扑排序,按顺序输出选择情况


#include<iostream>
#include<cstdio>
#include<cstring>
#include<stack>
#include<queue>
#include<cstdlib>
using namespace std;
const int maxn = 2005;

int n;
int st[maxn], ed[maxn];
vector<int> e[maxn], reverseE[maxn];
stack<int> s;
bool instack[maxn];
int dfn[maxn], low[maxn], scc[maxn], sccn, step;
int in[maxn], op[maxn];
int mark[maxn];


bool check(int x, int y){
    if (st[x] >= ed[y] || ed[x] <= st[y]) return false;
    return true;
}

void init(){
    scanf("%d", &n);
    int a, b, c, d, t;
    for (int i = 0; i < n; i++){
        scanf("%d:%d %d:%d %d", &a, &b, &c, &d, &t);
        int t1 = a*60+b, t2 = c*60+d;
        st[i*2] = t1; ed[i*2] = t1+t;
        st[i*2+1] = t2-t; ed[i*2+1] = t2;
    }
    for (int i = 0; i < n; i++){
        for (int j = 0; j < n; j++){
            if (i != j){
                if (check(2*i, 2*j)) e[2*i].push_back(2*j+1);
                if (check(2*i, 2*j+1)) e[2*i].push_back(2*j);
                if (check(2*i+1, 2*j)) e[2*i+1].push_back(2*j+1);
                if (check(2*i+1, 2*j+1)) e[2*i+1].push_back(2*j);
            }
        }
    }
}

void tarjan(int x){
    dfn[x] = low[x] = ++step;
    s.push(x); instack[x] = true;
    int sz = e[x].size();
    for (int i = 0; i < sz; i++){
        int to = e[x][i];
        if (!dfn[to]){
            tarjan(to);
            low[x] = min(low[x], low[to]);
        }
        else if (instack[to])
            low[x] = min(low[x], dfn[to]);
    }
    if (dfn[x] == low[x]){
        sccn++;
        int now = 0;
        do{
            now = s.top();
            s.pop();
            instack[now] = false;
            scc[now] = sccn;
        }while(now != x);
    }
}


void solve(){
    sccn = step = 0;
    memset(instack, false, sizeof(instack));
    memset(dfn, 0, sizeof(dfn));
    memset(low, 0, sizeof(low));
    memset(scc, 0, sizeof(scc));
    memset(in, 0, sizeof(in));
    for (int i = 0; i < 2*n; i++)
        if (!dfn[i]) tarjan(i);
    for (int i = 0; i < n; i++){
        if (scc[2*i] == scc[2*i+1]){
            printf("NO\n");
            return ;
        }
        op[scc[2*i]] = scc[2*i+1]; op[scc[2*i+1]] = scc[2*i];
    }
    printf("YES\n");
    for (int i = 0; i < 2*n; i++){
        int sz = e[i].size();
        for (int j = 0; j < sz; j++){
            int to = e[i][j];
            if (scc[i] != scc[to])
                reverseE[scc[to]].push_back(scc[i]), in[scc[i]]++;
        }
    }

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

    memset(mark, 0, sizeof(mark));
    while(!q.empty()){
        int from = q.front();
        q.pop();
        if (mark[from] == 0){
            mark[from] = 1;
            mark[op[from]] = -1;
        }
        int sz = reverseE[from].size();
        for (int i = 0; i < sz; i++){
            if (--in[reverseE[from][i]] == 0) q.push(reverseE[from][i]);
        }
    }

    int a, b, c, d;
    for (int i = 0; i < n; i++){
        if (mark[scc[2*i]] == 1){
            a = st[2*i]/60;
            b = st[2*i]%60;
            c = ed[2*i]/60;
            d = ed[2*i]%60;
        }
        else{
            a = st[2*i+1]/60;
            b = st[2*i+1]%60;
            c = ed[2*i+1]/60;
            d = ed[2*i+1]%60;
        }
        printf("%02d:%02d %02d:%02d\n", a, b, c, d);
    }
}

int main(){
    init();
    solve();
    return 0;
}


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值