zoukankan      html  css  js  c++  java
  • UVA 221 城市化地图(离散化思想)

    题意:

    给出若干个栋楼俯视图的坐标和面积,求从俯视图的南面(可以视为正视图)看过去到底能看到多少栋楼。

    输入第一个n说明有n栋楼,然后输入5个实数(注意是实数),分别是楼的左下角坐标(x,y), 然后楼的x方向的宽度,y方向的深度,还有楼的高度。

    按横坐标,横坐标同样按纵坐标排序输出所有能看到的楼。

     

     

    分析:

    先记录一个一开始就想错的做法:

    以为只要把x 和 width放大到到足够大(例如10000倍,倍数越高精度越高),然后排序填充一下数轴就可以,就可以解决x坐标是小数的问题。但这样打了一下,发现第一计算速度很慢(放大后n倍计算量也同时放大n^2倍), 第二是无法处理覆盖与重叠的部分, 只会保留数轴中最大的部分, 无法保留在高楼南边的矮楼,第三这种做法也是错误的,只要他是连续的,就算放大也不能完全保证算法正确性。

    正确的做法应该是将正视图x轴离散化:

    首先这题楼的可见性可以简化成建筑物南面的墙的可见性(可在上图感受一下),所以输入的深度这个属性是没有用的,可以用%*lf(在键盘上读取但不保存)在输入时忽略掉。

    接下来就是离散化的核心,提取正视图x轴有用的点,先无限个x的取值化为有限的关键点。

    ——按每栋楼的x的最左边和最右边看成区间的两个端点, 然后将整张图分为多个区间, 然后可以保证每个区间整片区间一定包含其中一栋楼, 只要判断这个区间包含的楼是否可见即可。

    分割如下:

    可以观察, 每个区间的任何一个点都拥有同样的性质,即无论从区间的哪一个点观察,都可得到同样的结果。

    不妨取每个区间的中点作为关键点

    可以先按输出要求遍历所有的楼,每栋楼再遍历所有的区间,然后再判断能否被看见。

    如何判断是否能看见?

    看见只要满足两个条件:

    设这栋楼为A。

    ①A在其中一个区间内(或者说忽略高度条件,在这个区间的中点能看见A), 即 楼的最左端 <=该区间中点 && 楼的最右端>=该区间中点 

    ②该区间内比A的y坐标小的楼都比A矮。(如果在A的南面有比他高的,那么自然看不见A)

      这个点又可以判断为两个条件

      1.是否有楼在这个区间内(判断方法同①,所以这种需要重复判断的条件最好写成一个函数

      2.该楼是否比A矮(判断一下高度即可)

    此外这题还涉及一个STL函数,unique,它通常使用在sort后的数组, 将不同的元素保留在数组前面, 然后那些重复元素移到后面。原型可以为unique(v.begin(),v.end()); 

    如果想更深入了解离散化思想可以看看matrix67的博客——http://www.matrix67.com/blog/archives/108

     

     1 #include<bits/stdc++.h>
     2 using namespace std;
     3 struct Bd
     4 {
     5     double x,y, width, high;
     6     int num;
     7     bool operator < (const Bd& rhs)const{
     8         return x<rhs.x ||(x==rhs.x && y < rhs.y);
     9     }
    10 }b[105];
    11 int n;
    12 bool can_see(int i, int mx)//判断第i栋楼在不在中点上
    13 {
    14     return b[i].x <= mx && (b[i].x + b[i].width >= mx);
    15 }
    16 bool visible(int i, int mx)//包含高度条件,在中点能不能看到第i栋楼
    17 {
    18     if(!can_see(i, mx)) return false;
    19     for(int k = 0; k < n;k++)
    20     {
    21         if(b[k].y < b[i].y && b[k].high >= b[i].high && can_see(k,mx)) return false;
    22     }
    23     return true;
    24 }
    25 int main()
    26 {
    27     #if LOCAL
    28     freopen("1.txt","r",stdin);
    29     #endif // LOCAL
    30     int kase = 1;
    31     while(scanf("%d", &n) && n)
    32     {
    33         double w[300];
    34         for(int i = 0; i < n ; i++)
    35         {
    36             double tx, ty, tw, th;
    37             scanf("%lf %lf %lf %*lf %lf", &tx, &ty, &tw, &th);
    38             b[i].x = tx;
    39             b[i].y = ty;
    40             b[i].width = tw;
    41             b[i].high = th;
    42             b[i].num = i+1;
    43             w[i*2] = tx;
    44             w[i*2+1] = tx + tw;
    45         }
    46         sort(b,b+n);
    47         sort(w,w+2*n);
    48         int m = unique(w,w+2*n) - w;
    49         if(kase != 1) printf("
    ");
    50         printf("For map #%d, the visible buildings are numbered as follows:
    %d",kase++, b[0].num);//第一栋楼在左下角,肯定能看到
    51         for(int i = 1; i < n; i++)
    52         {
    53             int is = 0;
    54             for(int j = 0; j < m - 1; j++)
    55             {
    56                 if(visible(i,(w[j]+w[j+1])/2))
    57                 {
    58                     is = 1;
    59                     break;
    60                 }
    61             }
    62             if(is) printf(" %d", b[i].num);
    63         }
    64         printf("
    ");
    65     }
    66 }
  • 相关阅读:
    理解和解决MySQL乱码问题
    搞清字符集和字符编码
    linux下卸载mysql
    mysqldump备份
    mysql 数据类型
    微信对接HIS——微信可查检验结果
    Install Haskell on Ubuntu and CentOS
    php用类生成二维码
    UVA Team Queue
    总有一种正能量触动你的心灵,读刘丁宁的一封信
  • 原文地址:https://www.cnblogs.com/Jadon97/p/6884505.html
Copyright © 2011-2022 走看看