poj1727 排序+二分搜索

/**
 * poj1727 排序+二分
 * 类似题目的附图,在每个点以正负1为斜率作直线,两条直线以下的点就可以作为它的因果点
 * 最大值就是所有点中最小的纵坐标;最小值,是最左的-1斜率直线与最右的+1斜率直线的交点
 * 对符合范围的t进行二分定位,每一级t计算能覆盖所有输入点的最小点数count,如果count > m则向上查找,反之向下查找。最后输出的就是满足count <= m的最大t值
 * 为了方便搜索,需要预先对所有点进行排序,排序的依据是,过该点的斜率为1的直线与x轴相交的点的x坐标,升序排列。排序之后,所有点是以与t=t0的左交点为准升序排列
 * count的计算有点像之前做过的一个dp题,每次记录一个覆盖区间,如果新的点在t=t0的覆盖段与记录的覆盖区间没有交点,那么启用新的覆盖区间,并count++;否则用二者的交集更新覆盖区间
 * 注意,我这道题用cin读就会超时,改用scanf就过了
 */
#include <iostream>
#include <stdlib.h>
#include <cstdio>
using namespace std;
const int MAX_NUM = 100002;
struct point
{
    int x;
    int t;
} p[MAX_NUM];
int n,m;
//以与x轴交点的左边点为准,升序排列
int cmp(const void* a,const void* b){
    return ((*((point*)a)).x - (*((point*)a)).t) - ((*((point*)b)).x - (*((point*)b)).t);
}

//检查当前的t值下,至少需要的点数是否<=m
bool check(int t){
    int count = 1;
    int last_left,last_right;
    for(int i=1;i<=n;i++){
        int left = t + p[i].x - p[i].t;
        int right = p[i].x + p[i].t - t;
        if(i == 1){
            last_right = right;
            last_left = left;
            continue;
        }
        //如果左点与之前的区间不相交,那么自增count,维护一个新的区间
        if(left > last_right){
            count ++;
            last_right = right;
            last_left = left;
        }
        //如果当前点的区间完全在之前区间内部,区间是缩小了,以当前点区间为备选区间
        else if(last_left <= left && last_right >= right){
            last_right = right;
            last_left = left;
        }
        //如果当前点的区间是与之前区间部分相交,那么只更新左点,以相交区间作为新的备选区间
        else if(last_left <= left && left <= last_right && last_right <= right){
            last_left = left;
        }

        if(count > m){
            return false;
        }
    }
    return true;
}

int main(){
    int max_times;
    cin >> max_times;
    for(int times = 1;times <= max_times;++times){
        int min_t = 99999999,search_top,search_bottom,leftest = 99999999,rightest = -99999999;
        cin >> n >> m;
        for(int i=1;i<=n;++i){
            //cin >> p[i].t >> p[i].x;
            scanf("%d%d",&p[i].t,&p[i].x);
            if(min_t > p[i].t){
                min_t = p[i].t;
            }
            leftest = (leftest > (p[i].t + p[i].x)) ? (p[i].t + p[i].x) : leftest;
            rightest = (rightest < (p[i].x - p[i].t)) ? (p[i].x - p[i].t) : rightest;
        }
        search_top = min_t;
        search_bottom = (rightest - leftest) / (-2);

        //sort
        qsort(p+1,n,sizeof(point),cmp);

        //half deviding!!
        int top = search_top, bottom = search_bottom, ans;
        while(top >= bottom){
            int search_t = (top + bottom) / 2;
            //如果这一层的点数不够,则向上查找
            if(check(search_t)){
                ans = search_t;//最大的点数不够的点满足条件
                bottom = search_t + 1;
            }
            //如果这一层的点数太多,则向下查找
            else{
                top = search_t - 1;
            }
        }
        cout << "Case " << times << ": " << ans << endl;
    }
    return 0;
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值