105 - The Skyline Problem

此题比较简单,直接扫描线法处理就可以了。先把每个楼的三元组 (left, height, right) 转化为两个事件:left 转化为进入事件,right 转化为离开事件。然后对所有的事件进行排序后从左向右进行扫描处理:

  • 一个楼进入扫描线:加入到“活动楼列表”,如果其 height > currentSkylineHeight,则修改 currentSkylineHeight height
  • 一个楼离开扫描线:从“活动楼列表”删除之,如果其 height == currentSkylineHeight,则在“活动楼列表”中找到最高的一个,若最高的那一个的 height’ < currrentSkylineHeight,调整currentSkylineHeight height’

要注意的点有几个:

  • 在一个 x 坐标处可能有多个楼进入,多个楼离开。要先处理进入的,再处理离开的。这可以在排序时定好规则。
  • 处理进入的楼时,要一次把所有在同一个 x 坐标处进入的楼全部扫描完,找到其中最高的高度与当前 skyline 的高度比较。一个 x 坐标处最多变化一次。
  • 处理离开的楼时,也与上面一样,注意一个 x 坐标处最多变化一次
  • 如果要在线提交得到AC,输出完成后必须加上个回车(我为此WA了很多次。。。)

扫描线在处理这种线段或是面积之类的问题时经常用到,多练习会对处理这类问题很有帮助。

 

返回 Volume I 索引

返回总索引

 

 

// // 105 - The Skyline Problem // Copyright (c) 2010 by Bo-wen Feng // [email protected] // #include #include #include #include #include #include #include #include #include #include #include #include #ifndef ONLINE_JUDGE #include std::ifstream cin("in.txt"); std::ofstream cout("out.txt"); // std::ofstream cout(stdout); #else #include #endif typedef long long llong; typedef unsigned long long ullong; typedef long double ldouble; using namespace std; #define LEFT 0 #define RIGHT 1 struct XEvent { int index; int x; int side; XEvent(int idx, int xValue, int s) : index(idx), x(xValue), side(s) {} }; bool compareXEvent(const XEvent& e1, const XEvent& e2) { if(e1.x != e2.x) return e1.x < e2.x; return e1.side < e2.side; } vector buildingHeights; vector events; set activeBuildings; vector skyline; int currentHeight; int main(int argc, char* argv[]) { int left, right, height; for(;;) { if(!(cin>> left>> height>> right)) break; if(left >= right) continue; int index = buildingHeights.size(); buildingHeights.push_back(height); events.push_back(XEvent(index, left , LEFT )); events.push_back(XEvent(index, right, RIGHT)); } // 按事件x坐标有序,x坐标相同的,进入的在前面,离开的在后面 sort(events.begin(), events.end(), compareXEvent); currentHeight = 0; for(int i = 0; i < events.size(); ++i) { int newHeight = buildingHeights[events[i].index]; int curX = events[i].x; if(events[i].side == LEFT) { // 处理所有进入扫描线的楼,找到其中最高的 activeBuildings.insert(events[i].index); while(i + 1 < events.size() && events[i+1].x == curX && events[i+1].side == LEFT) { ++i; newHeight = max(newHeight, buildingHeights[events[i].index]); activeBuildings.insert(events[i].index); } if(newHeight > currentHeight) { // 高度有变化才调整 currentHeight = newHeight; skyline.push_back(events[i].x); skyline.push_back(currentHeight); } } else { // 处理所有离开扫描线的楼,找到其中最高的 activeBuildings.erase(events[i].index); while(i + 1 < events.size() && events[i+1].x == curX && events[i+1].side == RIGHT ) { ++i; newHeight = max(newHeight, buildingHeights[events[i].index]); activeBuildings.erase(events[i].index); } if(newHeight == currentHeight) { // 离开的楼中最高的与当前轮廓线高一致,需要调整为当前活动楼房中最高的一个 int maxHeight = 0; set::iterator it = activeBuildings.begin(); for(; it != activeBuildings.end(); ++it) { int h = buildingHeights[*it]; if(h > maxHeight) maxHeight = h; } if(maxHeight < currentHeight) { // 高度有变化才调整 currentHeight = maxHeight; skyline.push_back(events[i].x); skyline.push_back(currentHeight); } } } } cout<< skyline[0]; for(int i = 1; i < skyline.size(); ++i) cout<< " "<< skyline[i]; cout<< "/n"; return 0; }   

你可能感兴趣的:(计算机科学)