题意:
N对夫妇计划在同一天结婚, 第i对夫妇计划在Si到Ti之间举行婚礼,第i对夫妇需要Di分钟来完成一个特殊的仪式,此外,这个仪式必须在婚礼的开始或结束时(即它必须从Si到Si + Di,或从Ti-Di到Ti)
问如何安排可以使John在每一个婚礼特别仪式上出席
分析:
我们需要在每一场婚礼的开始或结束中选择一段举行仪式,使得n场婚礼没有冲突
显然是一个2 SAT问题,那我们要如何建图呢?
实际上这道题和poj3207有异曲同工之妙
我们无脑暴力判断两个婚礼的仪式时间是否存在冲突
设
(a1,a2),(b1,b2)
(
a
1
,
a
2
)
,
(
b
1
,
b
2
)
如果
a1,b1
a
1
,
b
1
存在矛盾:
a1−>b2,b1−>a2
a
1
−
>
b
2
,
b
1
−
>
a
2
如果
a1,b2
a
1
,
b
2
存在矛盾:
a1−>b1,b2−>a2
a
1
−
>
b
1
,
b
2
−
>
a
2
如果
a2,b1
a
2
,
b
1
存在矛盾:
a2−>b2,b1−>a1
a
2
−
>
b
2
,
b
1
−
>
a
1
如果
a2,b2
a
2
,
b
2
存在矛盾:
a2−>b1,b2−>a1
a
2
−
>
b
1
,
b
2
−
>
a
1
这样无脑连一tong之后,有解的必然还是有解,无解的情况一定包含在内了
(也就是说,保证正确性)
tarjan缩点判断可行性
如果可行,我们就面临着一个新的问题:构造可行解
dalao说这种方法叫:拓扑排序染色法,实际上就是模拟一遍2 SAT的过程
我们建出新图(反向连边)进行top排序
遇到一个没有标记的结点,就标记为选择
之后把ta的对立结点设置为不可选择,沿着边传递不可选择的标记即可
tip
一开始我把小时和分钟分开考虑,非常 * *
实际上我们统一用分钟计时就好了
#include<cstring>
#include<cstdio>
#include<iostream>
using namespace std;
const int N=2005;
int n,st[N],tot=0,ste[N],tet=0,du[N],opp[N],col[N];
struct node{
int y,nxt;
};
node way[4000000],e[4000000];
struct point{
int s,t;
};
point p[N];
void add(int u,int w) {
tot++;way[tot].y=w;way[tot].nxt=st[u];st[u]=tot;
}
void _add(int u,int w) {
tet++;e[tet].y=w;e[tet].nxt=ste[u];ste[u]=tet;
du[w]++;
}
bool jiao(int a,int b) {
if (p[a].s>=p[b].t||p[a].t<=p[b].s) return 0;
return 1;
}
int low[N],dfn[N],clo=0,S[N],top=0,cnt=0,belong[N];
bool vis[N];
void tarjan(int now) {
S[++top]=now;
low[now]=dfn[now]=++clo;
vis[now]=1;
for (int i=st[now];i;i=way[i].nxt)
if (!dfn[way[i].y]) {
tarjan(way[i].y);
low[now]=min(low[now],low[way[i].y]);
}
else if (vis[way[i].y])
low[now]=min(low[now],dfn[way[i].y]);
if (low[now]==dfn[now]) {
cnt++;
int x=0;
while (x!=now) {
x=S[top--];
belong[x]=cnt;
vis[x]=0;
}
}
}
bool solve() {
for (int i=1;i<=n*2;i++)
if (!dfn[i])
tarjan(i);
for (int i=1;i<=n;i++)
if (belong[i]==belong[i+n]) return 0;
return 1;
}
void rebuild() {
for (int i=1;i<=2*n;i++)
for (int j=st[i];j;j=way[j].nxt)
if (belong[i]!=belong[way[j].y])
_add(belong[way[j].y],belong[i]); //反向连边
}
void dfs(int x) {
if (col[x]) return;
col[x]=-1; //标记为不可选
for (int i=ste[x];i;i=e[i].nxt)
dfs(e[i].y);
}
void TOP() {
top=0;
for (int i=1;i<=cnt;i++)
if (!du[i]) S[++top]=i;
while (top) {
int now=S[top--];
if (col[now]) continue;
col[now]=1;
dfs(opp[now]);
for (int i=ste[now];i;i=e[i].nxt)
{
du[e[i].y]--;
if (!du[e[i].y]) S[++top]=e[i].y;
}
}
}
void print(int x) {
printf("%.2d:%.2d %.2d:%.2d\n",p[x].s/60,p[x].s%60,p[x].t/60,p[x].t%60);
}
int main()
{
scanf("%d",&n);
for (int i=1;i<=n;i++) {
int a,b,c,d,t;
scanf("%d:%d %d:%d %d",&a,&b,&c,&d,&t);
p[i].s=a*60+b; p[i].t=p[i].s+t; //i,i+n属于一个集合
p[i+n].t=c*60+d; p[i+n].s=p[i+n].t-t;
}
for (int i=1;i<=n;i++)
for (int j=i+1;j<=n;j++) {
if (jiao(i,j)) add(i,j+n),add(j,i+n);
if (jiao(i,j+n)) add(i,j),add(j+n,i+n);
if (jiao(i+n,j)) add(i+n,j+n),add(j,i);
if (jiao(i+n,j+n)) add(i+n,j),add(j+n,i);
}
if (!solve()) {
printf("NO\n");
return 0;
}
else printf("YES\n");
rebuild();
for (int i=1;i<=n;i++) { //对立集合
opp[belong[i]]=belong[i+n];
opp[belong[i+n]]=belong[i];
}
TOP();
for (int i=1;i<=n;i++)
if (col[belong[i]]==1) //已选
print(i);
else print(i+n);
return 0;
}