您的位置:首页 > 其它

【UVA1146】NOW OR LATER 2-SAT问题

2015-09-07 19:58 309 查看
题目大意:有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(非原创……)

[code]#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;
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: