POJ 3683 Priest John's Busiest Day (2-SAT 输出路径)

来源:互联网 发布:skype for mac 下载 编辑:程序博客网 时间:2024/06/05 04:43

题目大意:

一个牧师要去主持婚礼,每对新人夫妻会给你两个时间和一个持续时间,分别为 from to 和value
你可以在from到from+value的时间段里给她们们举行婚礼,也可以在to-value到to的时间段里为她们举行婚礼,但是总是要选一个的。
然后现在给你n对夫妻的。问你能不能有一种可能的方案,如果有,按照输入的顺序,将每对夫妻举行婚礼的时间段输出出来。
第i对夫妻的两个时间段可以看成i和i+n这两个点,两个点中必选一个,典型的2-SAT问题。
关键是找到一组可行解。
总之大体思路即:通过矛盾关系建边,比如i和j有矛盾关系,也就是说这两个点不能同时选。所以建立i到j+n的边和j到i+n的边。然后tarjan跑强连通分量,对于每个点i,判断他和他所对应的点i+n是否在同一个联通分量里,如果在的话,说明存在矛盾,无解。否则,反向建图,拓扑排序,从入读为0的点开始选,如果没选过,那么直接选,然后对应着的新点肯定不选,然后从这个点把不选关系传递下去。

注意:

我们必须明确几个问题:
1. 由于2-SAT题目的特殊性,我们在建边的时候,总是建的对称的边,最后也肯定是一个对称的图,所以即使在经过缩点后,图依旧是对称的。
2. 缩点后正向建图也可以,只不过这时需要找出度为0的点,也不是不可以,只是你会发现你不容易删去跟此点有关的边,所以就很复杂。
3. 注意在构建新图的时候一定要注意重边,建出重边的话拓扑排序就会错。。

ac代码:

#include <iostream>#include <algorithm>#include <cstdio>#include <string.h>#include <queue>using namespace std;const int maxn = 2200;struct edge{    int from,to,v,next;}ed[40000000],a[2200];int head[maxn];int cnte,cnte2;void ae(int x,int y){    ed[++cnte].to = y;    ed[cnte].next = head[x];    head[x]=cnte;}int ma[2200][2200];// 缩点之后的新图(反向图)int col[2200];//染成的颜色 1代表一个集合 -1代表i+n的那集合 0代表未访问过int fanxiang[2200];int dfn[maxn],low[maxn],vis[maxn],stak[maxn],belong[maxn],cntc,cnts,index;//strong connected component //cnt of stackvoid dfs(int u){ //tarjan里的dfs    dfn[u]=low[u] = ++index;    stak[cnts++]=u;    vis[u]=1;    for(int i = head[u];i!=-1;i=ed[i].next){        int v = ed[i].to;        if(!dfn[v]){            dfs(v);            low[u] = min(low[u],low[v]);        }        else if(vis[v]){            low[u] = min(low[u],dfn[v]);        }    }    if(dfn[u]==low[u]){        cntc++;int v;        do{            v = stak[--cnts];            vis[v] = 0;            belong[v] = cntc;            fanxiang[cntc] = v;        }while(v!=u);    }}int n,m;void tarjan(){    for(int i = 1;i <= 2*n;i++){        if(!dfn[i]){            dfs(i);        }    }}void f(int i ,int j){// 建边 (构建必选关系)    int len1 = a[i].v,len2 = a[j].v;    if( !(a[i].from+len1 <= a[j].from || a[j].from+len2 <= a[i].from) ){        ae(i,j+n);ae(j,i+n);    }    if( !(a[i].from+len1 <= a[j].to-len2 || a[j].to <= a[i].from) ){        ae(i,j);ae(j+n,i+n);    }    if( !(a[i].to <= a[j].from || a[j].from+len2 <= a[i].to-len1) ){        ae(i+n,j+n);ae(j,i);    }    if( !(a[i].to <= a[j].to-len2 || a[j].to<= a[i].to-len1) ){        ae(i+n,j);ae(j+n,i);    }}int in[maxn];void dfscol(int u){//将不可选关系传递下去,    for(int i = 1;i <= cntc;i++){        if(ma[u][i] == 1 && !col[i]){            col[i] = -1;            dfscol(i);        }    }}int main(){    scanf("%d",&n);    memset(head,-1,sizeof(head));    memset(ma,0,sizeof(ma));    int h1,m1,h2,m2,value;    for(int i = 1;i <= n;i++){        scanf("%d:%d %d:%d %d",&h1,&m1,&h2,&m2,&value);        a[i].from = h1*60+m1;        a[i].to = h2*60+m2;        a[i].v = value;    }    for(int i = 1;i <= n;i++){        for(int j = i+1;j <= n;j++) f(i,j);    }    tarjan();    int flag = 1;    for(int i = 1;i <= n;i++){        if(belong[i] == belong[i+n]){            flag = 0;break;        }    }    if(flag == 0){        puts("NO");    }    else{        puts("YES");        queue<int> q;        for(int i = 1;i <= 2*n;i++){//反向建新图 同时进行入度统计            for(int j = head[i];j!=-1;j=ed[j].next){                int to = ed[j].to,ii = belong[i],jj = belong[to];                if(ii != jj && ma[jj][ii]==0){//判重边                    ma[jj][ii] = 1;in[ii]++;                }            }        }        for(int i = 1;i <= cntc;i++){//拓扑排序 取入读为0的点            if(in[i] == 0) q.push(i);        }        int top;        while(!q.empty()){//依次选择当前没有被选过的点 染上色1 并将他对立的新点染成-1            top = q.front();            q.pop();            for(int i = 1;i <= cntc;i++){                if(ma[top][i] == 1){                    in[i]--;                    if(in[i] == 0)                        q.push(i);                }            }            if(col[top] == 0){                col[top] = 1;                int to = fanxiang[top]; // 为了找到对立的新点 通过对立的原点来找                if(to > n) to -= n;                else to += n;                to = belong[to];col[to] = -1;                dfscol(to);            }        }        for(int i = 1;i <= n;i++){ // 对每个点(边) 判断他所处的新点的颜色,然后输出            if(col[belong[i]] == 1){                h1 = a[i].from/60;m1 = a[i].from % 60;                printf("%02d:%02d ",h1,m1);                a[i].from += a[i].v;                h1 = a[i].from/60;m1 = a[i].from % 60;                printf("%02d:%02d\n",h1,m1);            }            else{                a[i].to -= a[i].v;                h1 = a[i].to/60;m1 = a[i].to % 60;                printf("%02d:%02d ",h1,m1);                a[i].to += a[i].v;                h1 = a[i].to/60;m1 = a[i].to % 60;                printf("%02d:%02d\n",h1,m1);            }        }    }    return 0;}
原创粉丝点击