题目大意:有n架飞机需要着陆。每架飞机都可以选择“早着陆”和“晚着陆”两种方式之一,且必须选择一种。第i架飞机的早着陆时间为Ei,晚着陆时间为Li,不得在其他时间着陆。你的任务是为这些飞机安排着陆方式,使得整个着陆计划尽量安全。换句话说,如果把所有飞机的实际着陆时间安照从早到晚的顺序排列,相邻两个着陆时间间隔的最小值(称为安全间隔)应尽量的大。
输入格式:
输入包含若干组数据。每组数据第一行为飞机的数目n(2<=n<=2 000)。以下n行每行两个整数,即早着陆时间和晚着陆时间。所有时间t满足0<=t<=107。输入结束标志为文件结束符(EOF)。
输出格式
对于每组数据,输出安全间隔的最大值。
思路:刚开始看到这题时博主有同学断言他是NPC的= =
将飞机i拆为2*i和2*i+1表示早着陆和晚着陆(我们把2*i和2*i+1称为对称点,则点x与x^1互为对称点,x对称点简称为x’)。二分答案t,若点i,j的时间差小于t,则建边i→j’,j→i’,意思是选点i必选j的对称点,选点j必选i的对称点。求出所有强连通分量后,若对于任意的i,都有i与i’所在强连通分量不同,那么当前答案下一定有解,否则无解。
关于2-SAT可参考博主某处PPT(非原创……)
#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<cmath>
#define maxn 2000
#define clr(a) memset(a,0,sizeof(a))
#define inf 0x7fffffff/2
using namespace std;
int t[2*maxn+10],n;
struct EDGE{
int u,v,next;
}edge[maxn*maxn*4+10];
int head[2*maxn+10],pp;
void addedge(int u,int v){
edge[++pp]=(EDGE){u,v,head[u]};
head[u]=pp;
}
void build(int u,int lim){
for(int i=0;i<2*n;i++)if(u/2!=i/2){
if(abs(t[i]-t[u])<lim){
addedge(u,i^1);
}
}
}
int dfn[maxn*2+10],low[maxn*2+10],clo;
int sta[maxn*2+10],p;
int sccno[maxn*2+10],scnt;
void dfs(int u){
dfn[u]=low[u]=++clo;
sta[++p]=u;
for(int i=head[u];i;i=edge[i].next){
int v=edge[i].v;
if(!dfn[v]){
dfs(v);
low[u]=min(low[u],low[v]);
}else if(!sccno[v]){
low[u]=min(low[u],dfn[v]);
}
}
if(low[u]==dfn[u]){
scnt++;
for(;;){
int x=sta[p--];
sccno[x]=scnt;
if(x==u)break;
}
}
}
void findscc(int n){
clr(sccno);
clr(dfn);
clo=scnt=0;
for(int i=0;i<2*n;i++)if(!dfn[i])dfs(i);
}
bool judge(int x){
pp=0;
clr(head);
for(int i=0;i<2*n;i++){
build(i,x);
}
findscc(n);
for(int i=0;i<n;i++)if(sccno[2*i]==sccno[2*i+1])return 0;
return 1;
}
int main(){
freopen("1146.in","r",stdin);
while(scanf("%d",&n)==1){
int r=0;
for(int i=0;i<2*n;i++){
scanf("%d",&t[i]);
r=max(r,t[i]);
}
int l=0,ans=0;
while(l<=r){
int m=(l+r)/2;
if(judge(m)){
ans=m;
l=m+1;
}else r=m-1;
}
printf("%d\n",ans);
}
return 0;
}