poj 3683 2-SAT 拓扑排序输出

【题意】

有n个婚礼,每个婚礼有起始时间si,结束时间ti,还有一个主持时间ti,ti必须安排在婚礼的开始或者结束,主持由祭祀来做,但是只有一个祭祀,所以各个婚礼的主持时间不能重复,问你有没有可能正常的安排主持时间,不能输出no,能的话要输出具体的答案:即每个婚礼的主持时间段是什么样的。

【题解】

2-SAT判定问题,建图很简单,此略。

重点讲一下输出方案。

我们可以把处在同一个强连通分量中的点和边缩成一个点,得到新的有向图G1。然后,我们把G中的所有弧反向,得到图G2。
现在我们观察。由于已经进行了缩点的操作,因此G2中一定不存在圈,也就是说,具有拓扑结构。
我们把G中所有顶点置为“未着色”。按照拓扑顺序重复下面的操作:
1、选择第一个未着色的顶点x。把x染成红色。
2、把所有与x矛盾的顶点(如果存在bi,~bi∈B ,且bi属于x代表的强连通分量,~bi属于代表y的强连通分量,那么x和y就是互相矛盾的顶点)及其子孙全部全部染成蓝色。
3、重复操作1和2,直到不存在未着色的点为止。此时,G2中被染成红色的点在图G中对应的顶点集合,就对应着该2-SAT的一组解。
此实现可以递归。

详见代码中的draw(),del(),paint()。

【代码】

#include <iostream>
#include <vector>
using namespace std;
const int maxn=2005,maxe=maxn*maxn;

struct edge    
{    
       int x,y,next;    
}e[maxe];   
int dfn[maxn],low[maxn],v[maxn],s[maxn],b[maxn],h[maxn],g[maxn],col[maxn];  
int ss[maxn],tt[maxn],d[maxn],q[maxn];
int n,tot,cnt,times,t,tot2,st,ed;   
vector<int> ant[maxn];

void ins(int x,int y)    
{    
     e[++tot].x=x;e[tot].y=y;    
     e[tot].next=h[x];h[x]=tot;
}    
    
void tarjan(int x)    
{    
     int y,i;    
     times++;t++;    
     dfn[x]=low[x]=times;    
     v[x]=1;s[t]=x;    
     for (i=h[x];i;i=e[i].next)    
     {    
         y=e[i].y;    
         if (v[y]==0)    
         {    
            tarjan(y);    
            low[x]=min(low[x],low[y]);    
         }    
         if (v[y]==1)    
            low[x]=min(low[x],dfn[y]);    
     }    
     if (dfn[x]==low[x])    
     {    
        cnt++;    
        do    
        {    
              y=s[t--];    
              b[y]=cnt;v[y]=2;    
        }while (y!=x);    
     }    
} 

bool inter(int a,int b,int c,int d)
{
     if (b<=c || d<=a) return false;
     return true;
}

void build()
{
     int i,j;
     for (i=1;i<=n;i++)
        for (j=1;j<=n;j++)
        if (i!=j)
        {
           if (inter(ss[i],ss[i]+d[i],ss[j],ss[j]+d[j]))
              ins(i,j+n);
           if (inter(ss[i],ss[i]+d[i],tt[j]-d[j],tt[j]))
              ins(i,j);
           if (inter(tt[i]-d[i],tt[i],ss[j],ss[j]+d[j]))
              ins(i+n,j+n);
           if (inter(tt[i]-d[i],tt[i],tt[j]-d[j],tt[j]))
              ins(i+n,j);
        }
}

void init()
{
     scanf("%d\n",&n);
     for (int i=1;i<=n;i++)
     {
         int x1,y1,x2,y2;
         char ch;
         scanf("%d:%d %d:%d %d\n",&x1,&y1,&x2,&y2,&d[i]);
         ss[i]=x1*60+y1;tt[i]=x2*60+y2;
     }
}

void work()
{
     for (int i=1;i<=n+n;i++)
        if (v[i]==0)
           tarjan(i);
}

void rebuild()
{
     int i;
     for (i=1;i<=n;i++)
     {
         ant[b[i]].push_back(b[i+n]);
         ant[b[i+n]].push_back(b[i]);
     }
     memset(h,0,sizeof(h));
     tot2=tot;
     tot=0;
     for (i=1;i<=tot2;i++)
         if (b[e[i].x]!=b[e[i].y]) 
         {
            g[b[e[i].x]]++;
            ins(b[e[i].y],b[e[i].x]);
         }
}

void toposort()
{
    st=1;ed=0;
    int i;
    for (i=1;i<=cnt;i++)
        if (g[i]==0) q[++ed]=i;
    while (st<=ed)
    {
          int x=q[st++],y;
          for (i=h[x];i;i=e[i].next)
          {
              y=e[i].y;
              g[y]--;
              if (g[y]==0) q[++ed]=y;
          }
    }
}

void paint(int x)
{
     int i,y;
     for (i=h[x];i;i=e[i].next)
     {
         y=e[i].y;
         if (col[y]==0) 
         {
            col[y]=col[x];
            paint(y);
         }
     }
}

void del(int x)
{
     int i,y;
     for (i=0;i<ant[x].size();i++)
     {
         y=ant[x][i];
         if (col[y]==0)
         {
            col[y]=2;
            paint(y);
         }
     }
}

void draw()
{
     for (int i=1;i<=ed;i++)
     if (col[q[i]]==0)
     {
        col[q[i]]=1;
        del(q[i]);
     }
}

void print()
{
    cout << "YES\n";
    for (int i=1;i<=n;i++)
    {
        int x1,x2,y1,y2;
        if (col[b[i]]==1)
        {
           x1=ss[i]/60;x2=ss[i]%60;y1=(ss[i]+d[i])/60;y2=(ss[i]+d[i])%60;
        }
        else
        {
            x1=(tt[i]-d[i])/60;x2=(tt[i]-d[i])%60;y1=tt[i]/60;y2=tt[i]%60;
        }
        if (x1<10) cout << "0" << x1 << ":";
        else cout << x1 << ":";
        if (x2<10) cout << "0" << x2 << " ";
        else cout << x2 << " ";
        if (y1<10) cout << "0" << y1 << ":";
        else cout << y1 << ":";
        if (y2<10) cout << "0" << y2 << "\n";
        else cout << y2 << "\n";
    }
}

int main()
{
    freopen("pin.txt","r",stdin);
    freopen("pou.txt","w",stdout);
    int i;
    init();
    build();
    work();
    for (i=1;i<=n;i++)
        if (b[i]==b[i+n]) 
        {
           cout << "NO\n";
           return 0;
        }
    rebuild();
    toposort();
    draw();
    print();
    return 0;
}


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值