2-sat问题,要求要输出方案。
先考虑建模,把每个婚礼都拆成两个部分。
对于两个婚礼,A,B,用A,B表示开始,A’,B’表示结束,如果A与B,B’都冲突的话,A就不能选,连向A’,否则只有一个冲突的话,A连B’,A’连B。
这样图就建好了,那么如何输出方案呢,一般tarjan后跑一个拓扑序就好了,但是有更好的方案,tarjan相当于就是一个拓扑了,最后只要A与A’谁的强连通分量编号小就选谁就好了。
#include<cstdio>
#include<cstdlib>
#include<string>
#include<cstring>
#include<cmath>
#include<iostream>
#include<algorithm>
#define N 1000000
using namespace std;
int n,hh,mm,x;
int s,t,d,ans[N+5],m;
struct point{
int s,t;
};point a[2*N+5];
int first[N+5],nxt[N+5],to[N+5],siz;
int in[N+5],mark[N+5],stk[N+5],top,id;
int dfn[N+5],low[N+5],ind;
inline void link(int x,int y)
{
nxt[siz]=first[x];
first[x]=siz;
to[siz++]=y;
}
inline bool agst(point a,point b)
{
if(a.s>=b.s&&a.s<b.t)return true;
if(b.s>=a.s&&b.s<a.t)return true;
return false;
}
inline void tarjan(int x)
{
dfn[x]=low[x]=++ind;
stk[++top]=x,in[x]=true;
for(int i=first[x];i!=-1;i=nxt[i])
{
int u=to[i];
if(!dfn[u])tarjan(u),low[x]=min(low[x],low[u]);
else if(in[u])low[x]=min(low[x],dfn[u]);
}
if(dfn[x]==low[x])
{
id++;int tmp;
do
{
tmp=stk[top--];
mark[tmp]=id;
in[tmp]=false;
}while(tmp!=x);
}
}
int main()
{
freopen("in.txt","r",stdin);
scanf("%d",&n);
for(int i=0;i<n;i++)
{
scanf("%d:%d",&hh,&mm);s=hh*60+mm;
scanf("%d:%d",&hh,&mm);t=hh*60+mm;
scanf("%d",&d);
a[i*2].s=s,a[i*2].t=s+d;
a[i*2+1].s=t-d,a[i*2+1].t=t;
}
memset(first,-1,sizeof(first));
for(int i=0;i<2*n;i++)
for(int j=i+1;j<2*n;j++)
{
if(((i>>1)!=(j>>1))&&agst(a[i],a[j]))
{
if(agst(a[i],a[j^1]))link(i,i^1);
else link(i,j^1),link(j,i^1);
}
}
for(int i=0;i<2*n;i++)
if(!dfn[i])tarjan(i);
for(int i=0;i<2*n;i++)
if(mark[i]==mark[i^1])return cout<<"NO",0;
for(int i=0;i<2*n;i++)
if(mark[i]<mark[i^1])ans[++m]=i;
printf("YES\n");
for(int i=1;i<=m;i++)
{
x=ans[i];x=a[x].s;
hh=x/60,mm=x%60;
printf("%02d:%02d ",hh,mm);
x=ans[i];x=a[x].t;
hh=x/60,mm=x%60;
printf("%02d:%02d\n",hh,mm);
}
return 0;
}