例题5-12 城市正视图(Urban Elevations, ACM/ICPC World Finals 1992, UVa221)

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/richenyunqi/article/details/86499324

欢迎访问我的Uva题解目录哦 https://blog.csdn.net/richenyunqi/article/details/81149109

题目描述

例题5-12 城市正视图(Urban Elevations, ACM/ICPC World Finals 1992, UVa221)题目描述

题意解析

有n(n≤100)个建筑物。输入每个建筑物左下角坐标(即x、y坐标的最小值)、宽度(即x方向的长度)、深度(即y方向的长度)和高度(以上数据均为实数),输出正视图中能看到的所有建筑物,按照左下角x坐标从小到大进行排序。左下角x坐标相同时,按y坐标从小到大排序。
输入保证不同的x坐标不会很接近(即任意两个x坐标要么完全相同,要么差别足够大,不会引起精度问题)。

算法设计

借鉴了《算法竞赛与入门经典》中的解法:

判断可见性看上去比较麻烦,因为一个建筑物可能只有部分可见,无法枚举所有x坐标,来查看这个建筑物在该处是否可见,因为x坐标有无穷多个。解决方法有很多种,最常见的是离散化,即把无穷变为有限。
具体方法是:把所有x坐标排序去重,则任意两个相邻x坐标形成的区间具有相同属性,一个区间要么完全可见,要么完全不可见。这样,只需在这个区间里任选一个点(例如中点),就能判断出一个建筑物是否在整个区间内可见。如何判断一个建筑物是否在某个x坐标处可见呢?首先,建筑物的坐标中必须包含这个x坐标,其次,建筑物南边不能有另外一个建筑物也包含这个x坐标,并且不比它矮。

简单来说,就是把每个建筑物东西两个边的x坐标作为边界,将整个x轴分割成为一个个的小区间,再去一一枚举分别判断每个小区间内可见的建筑物。

C++代码

代码中使用了c++标准库中的unique函数,它能够“删除所有相邻的重复元素”。unique函数一般应用于有序容器,而且unique本身不会删除元素,而只是把重复元素移到了后面。该函数返回一个迭代器,迭代器指向的是重复元素的首地址

#include<bits/stdc++.h>
using namespace std;
struct Building{//建筑物类
    double x,y,w,d,h;
    int id;
};
int main(){
    int n;
    for(int ii=1;~scanf("%d",&n)&&n!=0;++ii){
        if(ii>1)//输出两个测试样例间的空行
            puts("");
        vector<Building>buildings(n);
        vector<double>x;//存储x轴坐标
        for(int i=0;i<n;++i){
            scanf("%lf%lf%lf%lf%lf",&buildings[i].x,&buildings[i].y,&buildings[i].w,&buildings[i].d,&buildings[i].h);
            buildings[i].id=i+1;
            x.push_back(buildings[i].x);
            x.push_back(buildings[i].x+buildings[i].w);
        }
        sort(buildings.begin(),buildings.end(),[](Building&a,Building&b){//排序
            return a.x<b.x||(a.x==b.x&&a.y<b.y);
        });
        sort(x.begin(),x.end());//对x坐标排序
        auto ending=unique(x.begin(),x.end())-x.begin();//第一个重复元素在x数组内的下标
        printf("For map #%d, the visible buildings are numbered as follows:\n",ii);
        bool space=false;//标志是否输出空格
        for(int i=0;i<buildings.size();++i){//遍历每一个建筑物
            for(int j=0;j<ending-1;++j){//遍历所有非重复的x坐标
                double mid=(x[j]+x[j+1])/2;//取区间内某一点
                if(buildings[i].x<=mid&&buildings[i].x+buildings[i].w>=mid){//该点在建筑物内
                    bool visible=true;//标志该建筑物是否可见
                    for(int k=0;k<buildings.size();++k)//遍历所有建筑物
                        if(buildings[k].x<=mid&&buildings[k].x+buildings[k].w>=mid//该点在建筑物内
                                &&buildings[k].y<buildings[i].y&&buildings[k].h>=buildings[i].h){//建筑物被遮盖
                            visible=false;//该建筑物不可见
                            break;
                        }
                    if(visible){//该建筑物可见
                        printf("%s%d",space==true?" ":"",buildings[i].id);//输出建筑物标号
                        space=true;
                        break;
                    }
                }
            }
        }
        puts("");
    }
    return 0;
}

猜你喜欢

转载自blog.csdn.net/richenyunqi/article/details/86499324