Now or later UVA - 1146(二分答案 && 2-sat)

Now or later UVA - 1146

题意:

有n架飞机需要着陆。每架飞机都可以选择“早着陆”和“晚着陆”两种方式之一,且必须选择一种。第 i i i架飞机的早着陆时间为 E i E_i Ei,晚着陆时间为 L i L_i Li,不得在其他时间着陆。你的任务是为这些飞机安排着陆方式,使得整个着陆计划尽量安全。换句话说,如果把所有飞机的实际着陆时间按照从早到晚的顺序排列,相邻两个着陆时间间隔的最小值(称为安全间隔)应尽量大。

思路:

看到最后一句话,显然是二分的套路。

那么现在问题是怎么安排:

  1. 可以二分答案,之后按照这个答案去check。(二分的中间值为mid)
  2. 对于一个飞机有 两个取值, 很容易联想到可以用 2-sat。
  3. 那么现在就是如何构造限制。其实分析也可以知道。两个飞机之间的着陆时间间隔 要大于等于 mid。(假如<mid,就要想办法让它们不成立,即限制)

假如两个着陆时间点的时间间隔,小于mid,那么就取另外两个方向去限制 。

反思:

  1. 考虑下面两种二分的写法。。。。。wa的教训。
		int l=0,r=0,ans;
        fori(i,0,n)cin>>ex[i][0]>>ex[i][1],r=max(r,max(ex[i][0],ex[i][1]));
        while(l<r){
            int mid=l+(r-l+1)/2;
            if(work(mid)){
                ans=mid;l=mid;//l=mid+1;
            }else r=mid-1;//r=mid-1;
        }
		fori(i,0,n)cin>>ex[i][0]>>ex[i][1],r=max(r,max(ex[i][0],ex[i][1]));
        while(l<=r){
            int mid=(l+r)>>1;//l+(r-l+1)/2;
            if(work(mid)){
                ans=mid;l=mid+1;//l=mid;
            }else r=mid-1;
        }
  1. 注意二分的上下限。

AC

#include <iostream>
#include <cstring>
#include <algorithm>
#include <vector>
#include <stack>
#include <cmath>
#define pb push_back
#define fzhead EDGE(int _to, int _next)
#define fzbody to(_to), next(_next)
#define mst(x,a) memset(x,a,sizeof(x))
#define For(i,x,y) for(int i=(x); i<=(y); i++)
#define fori(i,x,y) for(int i=(x); i<(y); i++)
using namespace std;
const int maxn=2000+10;
const int maxm=2000*2000+10;
struct EDGE{
    int to,next;
    EDGE(){}
    fzhead:fzbody{}
}e[2*maxm*2];
int head[2*maxn],tot;
bool mark[maxn*2];
///int s
stack<int>s;
void add(int bg, int to){
    e[tot]=EDGE(to,head[bg]);
    head[bg]=tot++;
}
void add_clause(int x, int xval, int y, int yval){
    x=x*2+xval;
    y=y*2+yval;
    add(x^1,y);
    add(y^1,x);
}
bool dfs(int x){
    if(mark[x^1])return false;
    if(mark[x])return true;
    mark[x]=true;
    s.push(x);
    for(int i=head[x]; i!=-1; i=e[i].next){
        int v=e[i].to;
        if(!dfs(v))return false;
    }
    return true;
}
int n;
bool solve(){
    for(int i=0; i<2*n; i+=2){//fori(i,0,2*n){
        if(!mark[i] && !mark[i+1]){
            while(!s.empty())s.pop();
            if(!dfs(i)){
                while(!s.empty())mark[s.top()] = false,s.pop();
                if(!dfs(i+1))return false;
            }
        }
    }
    return true;
}
void init(){
    mst(mark,0);tot=2;
    mst(head,-1);
    while(!s.empty())s.pop();
}
int ex[maxn][2];
bool work(int dis){
    init();
    fori(i,0,n)fori(a,0,2)fori(j,i+1,n)fori(b,0,2){
        if(abs(ex[i][a]-ex[j][b])<dis)add_clause(i,a^1,j,b^1);
    }
    return solve();
}
int main()
{
    ios::sync_with_stdio(0);cin.tie(0);cout.tie(0);
    while(cin>>n){
        int l=0,r=0,ans;
        fori(i,0,n)cin>>ex[i][0]>>ex[i][1],r=max(r,max(ex[i][0],ex[i][1]));
        while(l<=r){
            int mid=(l+r)>>1;//l+(r-l+1)/2;
            if(work(mid)){
                ans=mid;l=mid+1;//l=mid;
            }else r=mid-1;
        }
        cout<<ans<<endl;
    }
    return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值