洛谷P2218 [HAOI2007]覆盖问题
? 解题记录 ? ? 洛谷 ? ? 二分答案 ?    2018-10-21 15:00:09    460    0    0

题目描述

某人在山上种了N棵小树苗。冬天来了,温度急速下降,小树苗脆弱得不堪一击,于是树主人想用一些塑料薄膜把这些小树遮盖起来,经过一番长久的思考,他决定 用3个L*L的正方形塑料薄膜将小树遮起来。我们不妨将山建立一个平面直角坐标系,设第i棵小树的坐标为(Xi,Yi),3个L*L的正方形的边要求平行 与坐标轴,一个点如果在正方形的边界上,也算作被覆盖。当然,我们希望塑料薄膜面积越小越好,即求L最小值。

输入输出格式

输入格式:

 

第一行有一个正整数N,表示有多少棵树。

接下来有N行,第i+1行有2个整数Xi,Yi,表示第i棵树的坐标,保证不会有2个树的坐标相同。

 

输出格式:

 

一行,输出最小的L值。

 

输入输出样例

输入样例#1: 复制
4
0 1
0 -1
1 0
-1 0
输出样例#1: 复制
1

说明

数据范围

100%的数据,-1,000,000,000<=Xi,Yi<=1,000,000,000

30%的数据,N<=100

50%的数据,N<=2000

100%的数据,N<=20000

这么细节的题居然1A了,感觉手感回来了呀。

题目指名道姓用3个正方形覆盖肯定是有操作的。考虑最靠左的点,最靠上的点,最靠下的点,最靠右的点,有4个,那么其中的两个肯定要被同一个正方形覆盖,而且显然覆盖的最优策略是直接卡在边界上。那么我们直接枚举一次性覆盖哪两个点,将问题转化为用两个正方形去覆盖剩下的点,发现不是一个左上一个右下就是一个左下一个右上。就可以在很大很大常数的O(n log n)内解决这道题。

#include<cstdio>
#include<algorithm>
#include<cstring>
using namespace std;
const int maxn = 2e4 + 5;
const int inf = 0x3f3f3f3f;

struct Point {
    int pos[2];
} pt[maxn];

int mn[2], mx[2], nmn[2], nmx[2], n, vis[maxn];

bool in(const int &x, const int &y, const int &L, const int *pos) {
    return pos[0] <= x + L && pos[0] >= x && pos[1] <= y + L && pos[1] >= y;
}

void del(const int &x, const int &y, const int &L, int & cover) {
    for(register int i = 1; i <= n; ++i)
        if(in(x, y, L, pt[i].pos)) 
            if(!vis[i]) vis[i] = 1, ++cover;
}

void rewM(int * mn, int * mx) {
    mn[0] = mn[1] = inf;
    mx[0] = mx[1] = -inf;
    for(register int i = 1; i <= n; ++i) {
        if(vis[i]) continue;
        mn[0] = min(pt[i].pos[0], mn[0]);
        mn[1] = min(pt[i].pos[1], mn[1]);
        mx[0] = max(pt[i].pos[0], mx[0]);
        mx[1] = max(pt[i].pos[1], mx[1]);
    }
}

bool check2(int x, int y, int L) {
    int cover = 0;
    memset(vis, 0, sizeof(vis));
    del(x, y, L, cover);
    rewM(mn, mx);
    if(cover == n) return 1;
    del(mn[0], mn[1], L, cover);
    if(cover == n) return 1;
    del(mx[0] - L, mx[1] - L, L, cover);
    return cover == n;
}

bool check3(int x, int y, int L) {
    int cover = 0;
    memset(vis, 0, sizeof(vis));
    del(x, y, L, cover);
    rewM(mn, mx);
    if(cover == n) return 1;
    del(mn[0], mx[1] - L, L, cover);
    if(cover == n) return 1;
    del(mx[0] - L, mn[1], L, cover);
    return cover == n;
}

bool check(int L) {
    if(check2(nmn[0], nmn[1], L) || check3(nmn[0], nmn[1], L)) return true;
    if(check2(nmn[0], nmx[1] - L, L) || check3(nmn[0], nmx[1] - L, L)) return true;
    if(check2(nmx[0] - L, nmn[1], L) || check3(nmx[0] - L, nmn[1], L)) return true;
    if(check2(nmx[0] - L, nmx[1] - L, L) || check3(nmx[0] - L, nmx[1] - L, L)) return true;
    return false;
}

int DV(int L, int R) {
    while(L < R - 1) {
        int mid = L + R >> 1;
        if(check(mid)) R = mid;
        else L = mid;
    }
    return R;
}

int main() {
    scanf("%d", &n);
    for(register int i = 1; i <= n; ++i)
        scanf("%d%d", &pt[i].pos[0], &pt[i].pos[1]);
    rewM(nmn, nmx);
    printf("%d\n", DV(0, 2000000000));
    return 0;
}


上一篇: 洛谷P1640 [SCOI2010]连续攻击游戏

下一篇: 洛谷P2039 [AHOI2009]跳棋

460 人读过
立即登录, 发表评论.
没有帐号? 立即注册
0 条评论
文档导航