【UVA1146】NOW OR LATER 2-SAT问题
来源:互联网 发布:ios上玩的编程游戏 编辑:程序博客网 时间:2024/05/22 16:56
题目大意:有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/2using 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;}
0 0