zoukankan      html  css  js  c++  java
  • 【二分 贪心】覆盖问题 BZOJ1052 HAOI2007

    覆盖问题 bzoj1052

    题目来源:HAOI 2007

    题目描述

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

    输入输出

    input

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

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

    output

    一行,输出最小的L值

    样例

    input

    4
    0 1
    0 -1
    1 0
    -1 0

    output

    1 

    数据范围

    100%的数据,N<=20000

    思路

    确定在一定范围内有一些点,然后用边长为常数k(<边界范围)的三个正方形去覆盖它们的话,如果有合法的方案,那么一定存在至少一个正方形,它的两条边分别卡在两个边界上。 
    这个性质非常容易证明。因为如果确定是上下左右的边界,那么每一个边界上至少有一个点需要去覆盖。然而我们只有三个正方形,若想要覆盖这四个点,一定存在一个正方形覆盖了两个点,那么它就一定卡在两个边界上。如果正方形数少的话就更显然了。 
    做法:二分出一个答案k,然后dfs判断。dfs每一次放一个正方形,枚举它卡着当前区域的哪两个边界即可。

    代码

    #include <iostream>
    #include <cstdio>
    #include <cstdlib>
    #include <algorithm>
    #include <cstring>
    
    #define N 22222
    #define INF 2147483647
    
    using namespace std;
    /*
    首先,求出所有点的4个边界值形成的一个矩形,第一个正方形的一个边界一定与这个矩形的4个角中的一个重合,枚举4次即可,
    然后再找到剩下的点中的边界,重复一遍上面的操作,最后判断一下一个正方形是否可以覆盖剩余的所有矩形
    */
    struct P
    {
        int x,y;
    }p[N],p1[N],p2[N];
    
    int n;
    
    inline void read()
    {
        scanf("%d",&n);
        for(int i=1;i<=n;i++) scanf("%d%d",&p[i].x,&p[i].y);
    }
    
    inline bool check(int len)
    {
        if(n==0) return true;
        int sd[4][4];
        sd[1][1]=INF; sd[1][2]=-INF;
        sd[2][1]=INF; sd[2][2]=-INF;
        for(int i=1;i<=n;i++)
        {
            sd[1][1]=min(sd[1][1],p[i].x); sd[1][2]=max(sd[1][2],p[i].x);
            sd[2][1]=min(sd[2][1],p[i].y); sd[2][2]=max(sd[2][2],p[i].y);
        }
        for(int i=1;i<=2;i++)
            for(int j=1;j<=2;j++)
            {
                int m=0;
                for(int k=1;k<=n;k++)
                    if(abs(p[k].x-sd[1][i])>len||abs(p[k].y-sd[2][j])>len) p1[++m]=p[k];
                if(m==0) return true;
                
                int sp[4][4];
                sp[1][1]=INF; sp[1][2]=-INF;
                sp[2][1]=INF; sp[2][2]=-INF;
                for(int k=1;k<=m;k++)
                {
                    sp[1][1]=min(sp[1][1],p1[k].x); sp[1][2]=max(sp[1][2],p1[k].x);
                    sp[2][1]=min(sp[2][1],p1[k].y); sp[2][2]=max(sp[2][2],p1[k].y);
                }
                for(int ii=1;ii<=2;ii++)
                    for(int jj=1;jj<=2;jj++)
                    {
                        int s=0;
                        for(int kk=1;kk<=m;kk++)
                            if(abs(p1[kk].x-sp[1][ii])>len||abs(p1[kk].y-sp[2][jj])>len) p2[++s]=p1[kk];
                        if(s==0) return true;
                        
                        int sq[4][4];
                        sq[1][1]=INF; sq[1][2]=-INF;
                        sq[2][1]=INF; sq[2][2]=-INF;
                        for(int kk=1;kk<=s;kk++)
                        {
                            sq[1][1]=min(sq[1][1],p2[kk].x); sq[1][2]=max(sq[1][2],p2[kk].x);
                            sq[2][1]=min(sq[2][1],p2[kk].y); sq[2][2]=max(sq[2][2],p2[kk].y);
                        }
                        if(sq[2][2]-sq[2][1]<=len&&sq[1][2]-sq[1][1]<=len) return true;
                    }
            }
        return false;
    }
    
    inline void go()
    {
        int l=0,r=2000000000,mid,ans;
        while(l<=r)
        {
            mid=(l+r)>>1;
            if(check(mid)) ans=mid,r=mid-1;
            else l=mid+1;
        }
        printf("%d
    ",ans);
    }
    
    int main()
    {
        read(),go();
        return 0;
    }
    

    参考:http://www.cnblogs.com/proverbs/archive/2013/03/12/2956827.html

  • 相关阅读:
    Netty实现客户端和服务端通信简单例子
    上拉电阻的作用
    c语言常量指针赋值给变量指针导致警告
    修改ultisnips的默认键
    为debian8.2更换官方源
    linux下添加用户到sudo组
    用rfkill命令管理蓝牙和wifi
    用platformio编写arduino程序
    ubuntu下gcc-avr安装
    UNIX环境高级编程(第三版)关于apue.h的用法
  • 原文地址:https://www.cnblogs.com/bbqub/p/7673785.html
Copyright © 2011-2022 走看看