【问题标题】:skyline algorithm天际线算法
【发布时间】:2011-03-13 15:27:58
【问题描述】:

如何找到围绕此图像中轮廓的虚线的顶点?

上述示例的可能输入是:

宽度高度位置 3 9 17 5 9 9 12 4 8 3 11 3 10 7 1 2 3 19

所以对于这个例子,解决方案是

[(1, 0), (1, 7), (3, 7), (3, 11), (6, 11), (6, 7), (9, 7), (9, 9), (14, 9), (14, 4), (17, 4), (17, 9), (20, 9), (20, 3), (21, 3), (21, 0)]

【问题讨论】:

标签: c algorithm


【解决方案1】:

这很简单。创建一个 X 轴长度的数组,初始化为 0。在读取输入时,如果高度 >= 数组中该位置的当前值,则将高度写入此数组。

然后只循环遍历数组,每次值变化都是一个顶点。

基本上:

int heights[SIZE] = {0};
int i, width, pos, height, prev = -1;
while (scanf("%d %d %d", &width, &height, &pos) == 3) {
    for (i = 0; i < width; ++i) {
        if (heights[pos+i] < height)
            heights[pos+i] = height;
    }
}

for (i = 0; i < SIZE; ++i) {
    if (heights[i] != prev) {
        printf("(%d,%d) ", i+1, heights[i]);
        prev = heights[i];
    }
}
printf("\n");

【讨论】:

  • 这是一个很好的解决这个问题的方法,它是ACM比赛中的经典问题。这样一来,即使是大集合,您也能成功。
  • 这是 O(n^2) 而问题可以在 O(n log n) 中解决
【解决方案2】:

在幼稚的情况下,这似乎不是一个非常困难的算法。你知道输入大小是否会变大/有多大?

我最初的尝试:尝试从左向右移动。首先选择原点线上存在的最左边的块。爬到它的顶部。查找当前点和当前块右上点之间的左边缘的所有块。在该集合中,选择最接近的(但检查边缘情况,双关语不是故意的)。如果集合是空的,开始沿着块的右侧向下移动,寻找可以拦截的其他块。

基本上这就是你用眼睛追踪它的方式。

您可以通过保留排序列表然后搜索列表而不是查找集合并四处挖掘来进行一些简单的优化。例如,您可能会保留 4 个已排序的块列表,每个列表都按其中一侧的 x 或 y 坐标排序。

如果你有很多很多的块,你可以考虑使用多维数据结构来进一步组织信息。

【讨论】:

    【解决方案3】:

    我使用扫描线算法解决了这个问题。这是一个python类解决方案。

    有两个键: 1)使用变量“points”保存所有左右点及其高度和高度的符号来表示点是左还是右。 2)变量“active”用于保存所有已扫描的活动行。

    类解决方案: # @param {integer[][]} 建筑物 # @return {整数[][]} def getSkyline(自我,建筑物): 如果 len(建筑物)==0:返回 [] 如果 len(buildings)==1: 返回 [[buildings[0][0], building[0][2]], [buildings[0][1], 0]]

        points=[]
        for building in buildings:
            points+=[[building[0],building[2]]]
            points+=[[building[1],-building[2]]] # the negative sign means this point is a right point
        points=sorted(points, key=lambda x: x[0])
    
        moving, active, res, current=0, [0], [],-1
    
        while moving<len(points):
            i=moving
            while i<=len(points):
                if i<len(points) and points[i][0]==points[moving][0]:
                    if points[i][1]>0:
                        active+=[points[i][1]]
                        if points[i][1]>current:
                            current=points[i][1]
                            if len(res)>0 and res[-1][0]==points[i][0]: 
                                res[-1][1]=current
                            else:
                                res+=[[points[moving][0], current]]
                    else:
                        active.remove(-points[i][1]) #remove height of the lines than have been finished with scanning
                    i+=1
                else:
                    break
            if max(active)<current:
                current=max(active)
                res+=[[points[moving][0], current]] 
            moving=i
        return res 
    

    【讨论】:

      【解决方案4】:

      我创建了一个 Java 类来尝试解决这个问题。该类包括生成、求解和打印数据集的方法。我没有进行广泛的测试,可能还有一些错误。此外,我的解决方案可能不必要地复杂,但它的设计(理论上)适用于非离散高度和坐标值。

      import java.util.Random;
      
      public class Skyline {
      
          private int[][] buildings;
          private int[][] skyline;
      
          private int maxLength;
          private int maxHeight;
      
          public Skyline(int buildings, int maxLength, int maxHeight) {
              this.maxLength = maxLength;
              this.maxHeight = maxHeight;
              makeRandom(buildings);
          }
      
          public Skyline(int[][] buildings, int dimensions) {
              this.maxLength = maxLength;
              this.maxHeight = maxHeight;
              this.buildings = buildings;
          }
      
          public void makeRandom(int buildings) {
              this.buildings = new int[buildings][3];
      
              Random rand = new Random();
      
              for(int i = 0; i < buildings; i++) {
                  int start = rand.nextInt(maxLength-3);
                  int end = rand.nextInt(maxLength - start - 1) + start + 1;
                  int height = rand.nextInt(maxHeight-1) + 1;
      
                  this.buildings[i][0] = start;
                  this.buildings[i][1] = height;
                  this.buildings[i][2] = end; 
              }
      
              boolean swapped = true;
              while(swapped) {
                  swapped = false;
                  for(int i = 0; i < this.buildings.length-1; i++) {
                      if(this.buildings[i][0] > this.buildings[i+1][0]) {
                          swapped = true;
                          int[] temp = this.buildings[i];
                          this.buildings[i] = this.buildings[i+1];
                          this.buildings[i+1] = temp;
                      }
                  }
              }
      
      //        this.buildings[0][0] = 2;
      //        this.buildings[0][1] = 3;
      //        this.buildings[0][2] = 8;
          }
      
          public void printBuildings() {
              print(this.buildings, false);
          }
          public void printSkyline() {
              print(this.buildings, true);
          }
      
          public void print(int[][] buildings, boolean outline) {
              char[][] str = new char[this.maxLength][this.maxHeight];
              for(int i = 0; i < this.maxLength; i++) {
                  for(int j = 0; j < this.maxHeight; j++) {
                      str[i][j] = '.';
                  }
              }
      
              for(int i = 0; i < buildings.length; i++) {
                  int start = buildings[i][0];
                  int height = buildings[i][1];
                  int end = buildings[i][2];
      
                  //print the starting vertical
                  for(int j = 0; j < height; j++) {
                      if(outline) str[start][j] =  str[start][j] == '|' ? '.' : '|';
                      else str[start][j] = '|';
                  }
      
                  //print the ending vertical
                  for(int j = 0; j < height; j++) {
                      if(outline) str[end][j] = str[end][j] == '|' ? '.' : '|';
                      else str[end][j] =  '|';
                  }
      
                  //print the horizontal
                  if(height > 0) {
                      for(int j = start; j <= end; j++) {
                          str[j][height] = str[j][height] == '|' ? '|' : '-';
                      }
                  }
      
              }
      
              for(int i = maxHeight-1; i >= 0; i--) {
                  for(int j = 0; j < maxLength; j++) {
                      System.out.print(str[j][i]);
                  }
                  System.out.println();
              }
      
              System.out.println();
          }
      
          public void solveSkyline() {
      
              for(int i = 0; i < buildings.length; i++) {
                  boolean reduced = true;
                  while(reduced) {
                      reduced = false;
                      for(int j = i+1; j < buildings.length; j++) {
                          if(buildings[j][0] < buildings[i][2] && buildings[j][1] > buildings[i][1] && buildings[j][2] >= buildings[i][2]) { //if intersecting building is taller, and longer
                              buildings[i][2] = buildings[j][0];  
                              reduced = true;
                              break;
                          } else if(buildings[j][0] < buildings[i][2] && buildings[j][1] <= buildings[i][1] && buildings[j][2] >= buildings[i][2]) { //intersecting building is shorter, but longer
                              buildings[j][0] = buildings[i][2]; 
                              reduced = true;
                              break;
                          } else if(buildings[j][0] < buildings[i][2] && buildings[j][1] > 0 && buildings[j][1] < buildings[i][1] && buildings[j][2] <= buildings[i][2]) {  //building is invisible, so ignore it
                              buildings[j][1] = 0;
                              reduced = true;
                              break;
                          } else if(buildings[j][0] < buildings[i][2] && buildings[j][2] <= buildings[i][2] && buildings[j][1] > buildings[i][1]) {
                              int[] newBuilding = new int[]{buildings[j][2], buildings[i][1], buildings[i][2]};
                              int[][] newBuildings = new int[buildings.length+1][3];
                              boolean inserted = false;
                              buildings[i][2] = buildings[j][0];       
                              for(int k = 0; k < buildings.length; k++) {
                                  if(inserted == false) {
                                      if(newBuilding[0] < buildings[k][0]) {
                                          newBuildings[k] = newBuilding;
                                          newBuildings[k+1] = buildings[k];
                                          inserted = true;
                                      } else {
                                          newBuildings[k] = buildings[k];
                                      }
                                  } 
                                  if(inserted == false && k == buildings.length - 1) {
                                      newBuildings[k+1] = newBuilding;
                                  } else {
                                      newBuildings[k+1] = buildings[k];
                                  }
                              }
                              buildings = newBuildings;
                              reduced = true;
                              break;
                          }
                      }
                  }
              }
          }
      
          public static void main(String args[]) {
              Skyline s = new Skyline(5, 100, 10);
      
              s.printBuildings();
              s.solveSkyline();
              s.printBuildings();
      
              s.printSkyline();
          } 
      }
      

      【讨论】:

        【解决方案5】:

        我对这里描述的问题的解决方案https://leetcode.com/problems/the-skyline-problem/ 它对建筑物列表进行了两次迭代,但是这可以合并为一次迭代。但是,如果您考虑此处解释的纯算法解决方案http://www.algorithmist.com/index.php/UVa_105

        ,则有更优化的方法
        class Solution {
        public:
            vector<pair<int, int>> getSkyline(vector<vector<int>>& buildings) {
                // The final result.
                vector<pair<int, int>> result;
        
                // To hold information about the buildings
                std::set<BuildingInformation> buildingInformation;
        
                // Go through each building, and store information about the start and end heights.
                for ( vector<vector<int>>::iterator buildingIt = buildings.begin( ); buildingIt != buildings.end( ); ++buildingIt ) {
                    BuildingInformation buildingStart;
                    buildingStart.x = (*buildingIt)[0];
                    buildingStart.h = (*buildingIt)[2];
                    buildingStart.StartOrEnd = Start;
                    buildingInformation.insert(buildingStart);
                    buildingStart.x = (*buildingIt)[1];
                    buildingStart.StartOrEnd = End;
                    buildingInformation.insert(buildingStart);
                }
        
                // Keep track of the current height.
                int currentHeight = 0;
        
                // A map of active building heights against number of buildings (to handle multiple buildings overlapping with same height).
                // As it is a map, it'll be sorted by key, which is the height.
                std::map<int, int> heights;
        
                // Go through each building information that we generated earlier.
                for ( std::set<BuildingInformation>::iterator it = buildingInformation.begin( ); it != buildingInformation.end( ); ++it ) {
                    if ( it->StartOrEnd == Start ) {
                        // This is a start point, do we have this height already in our map?
                        if ( heights.find( it->h ) != heights.end( ) ) {
                            // Yes, increment count of active buildings with this height/
                            heights[ it->h ] += 1;    
                        } else {
                            // Nope, add this building to our map.
                            heights[ it->h ] = 1;  
                        }
        
                        // Check if building height is taller than current height.
                        if ( it->h > currentHeight ) {
                            // Update current height and add marker to results.
                            currentHeight = it->h;
                            result.push_back( pair<int, int>( it->x, currentHeight ) );
                        }
                    } else {
                        // This is an end point, get iterator into our heights map.
                        std::map<int, int>::iterator heightIt = heights.find( it->h );
        
                        // Reduce by one.
                        heightIt->second -= 1;
        
                        // If this was the last building of the current height in the map...
                        if ( heightIt->second == 0 ) {
                            // Remove from heights map.
                            heights.erase( heightIt );
        
                            // If our height was the current height...
                            if ( it->h == currentHeight ) {
                                // If we have no more active buildings...
                                if ( heights.size( ) == 0 ) {
                                    // Current height is zero.
                                    currentHeight = 0;
                                } else {
                                    // Otherwise, get iterator to one past last.
                                    heightIt = heights.end( );
        
                                    // Go back to get last valid iterator.
                                    --heightIt;
        
                                    // Store current height.
                                    currentHeight = heightIt->first;
                                }
        
                                // Add marker to results.
                                result.push_back( pair<int, int>( it->x, currentHeight ) );
                            }
                        }
                    }
                }
        
                return result;
            }
        private:
            // Is this a building start or end?
            enum BuildingStartOrEnd
            {
                Start = 0,
                End
            };
        
            // Information about building, there are two of these for each building, one for start, one for end.
            struct BuildingInformation
            {
                int x;
                int h;
                BuildingStartOrEnd StartOrEnd;
        
                // The ordering algorithm for the key, the rules we want to implement is keys are put in X order, and
                // in the case of a tie (x values the same), we want Start pieces to come before End pieces (this is
                // to handle cases where an old building ends and a new building begins on same X index, in which case
                // we want to process the new start before processing the old end), however if we have two Start pieces
                // at the same index, we wish to favour taller pieces (in this scenario we want to add a marker for the
                // tallest building), finally if we have two End pieces at the same index, we wish to prefer lower
                // pieces, as when multiple buildings end, we only want to add one result for the ultimate lowest point.
                bool operator < ( const BuildingInformation & rhs ) const
                {
                    if ( x == rhs.x )
                    {
                        if ( StartOrEnd == rhs.StartOrEnd ) {
                            if ( StartOrEnd == Start )
                                return h > rhs.h;
                            else
                                return h < rhs.h;
                        } else {
                            return StartOrEnd < rhs.StartOrEnd;
                        }
                    }
        
                    return x < rhs.x;
                }
            };
        };
        

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 2018-09-11
          • 2010-11-07
          • 2015-09-13
          • 2013-02-05
          • 1970-01-01
          • 2013-03-29
          • 1970-01-01
          相关资源
          最近更新 更多