2020-1ν•™κΈ°, λŒ€ν•™μ—μ„œ β€˜μ•Œκ³ λ¦¬μ¦˜β€™ μˆ˜μ—…μ„ λ“£κ³  κ³΅λΆ€ν•œ λ°”λ₯Ό μ •λ¦¬ν•œ κΈ€μž…λ‹ˆλ‹€. 지적은 μ–Έμ œλ‚˜ ν™˜μ˜μž…λ‹ˆλ‹€ :)

9 minute read

2020-1ν•™κΈ°, λŒ€ν•™μ—μ„œ β€˜μ•Œκ³ λ¦¬μ¦˜β€™ μˆ˜μ—…μ„ λ“£κ³  κ³΅λΆ€ν•œ λ°”λ₯Ό μ •λ¦¬ν•œ κΈ€μž…λ‹ˆλ‹€. 지적은 μ–Έμ œλ‚˜ ν™˜μ˜μž…λ‹ˆλ‹€ :)

λ°±μ€€ 1933번: 슀카이라인 문제λ₯Ό λ‹€λ£¨λŠ” ν¬μŠ€νŠΈμž…λ‹ˆλ‹€ πŸ™Œ

Given n rectangular buildings in a 2-dimensional city, computes the skyline of these buildings, eliminating hidden lines. The main task is to view buildings from a side and remove all sections that are not visible.

All buildings share common bottom and every building is represented by triplet (left, ht, right)

  • β€˜left’: is x coordinated of left side (or wall).
  • β€˜right’: is x coordinate of right side
  • β€˜ht’: is height of building.

A skyline is a collection of rectangular strips. A rectangular strip is represented as a pair (left, ht) where left is x coordinate of left side of strip and ht is height of strip.

- geeksforgeeks

<Skyline Problem>은 μ—¬λŸ¬ 개의 μ§μ‚¬κ°ν˜• λΉŒλ”©μ—μ„œ κ·Έλ“€μ˜ ν…Œλ‘λ¦¬μΈ skyline을 κ³„μ‚°ν•˜λŠ” λ¬Έμ œμ΄λ‹€. 좜λ ₯ ν˜•μ‹μ„ 보면 μ•Œ 수 μžˆλ“―μ΄ 높이(ht)κ°€ λ³€ν•  λ•Œμ˜ x μ§€μ λ§Œ (x, ht)둜 좜λ ₯ν•΄μ£Όλ©΄ λœλ‹€. <Brute Force>와 <Divide and Conquery>둜 문제λ₯Ό ν•΄κ²°ν•΄λ³΄μž!


Brute Force

1. Mark key points for each given building.

[left, ht, right] ν˜•μ‹μ˜ 각 λΉŒλ”©μ„ [left, ht], [right, 0] ν˜•μ‹μœΌλ‘œ λ°”κΏ”μ€€λ‹€.

2. For each marked key point, if there exist heighter key points overlap it, then change its y as the height of tallest overlapping building.

μ™Όμͺ½μ—μ„œ 였λ₯Έμͺ½ λ°©ν–₯으둜 천천히 λ”°λΌκ°€λ³΄μž!

λ¨Όμ € μ΄ˆλ‘μƒ‰ λΉŒλ”©μ˜ [left, ht] 점은 빨간색 λΉŒλ”©μ— overlapping 되기 λ•Œλ¬Έμ— y 값을 κ°±μ‹ ν•œλ‹€. μ΄λ•Œ, [left, ht] κ°’ 자체λ₯Ό κ°±μ‹ ν•˜λŠ”κ²Œ μ•„λ‹ˆλΌ κ°±μ‹ λœ [left, ht_new]λ₯Ό result array vector<point> ret에 넣어두면 λœλ‹€.

이 방법을 κ³„μ†ν•˜λ©΄β€¦

λͺ¨λ“  key pointλ₯Ό λ‹€ λŒμ•˜λ‹€λ©΄, vector<point> retμ—μ„œ y 값이 μ€‘λ³΅λ˜λŠ” 점듀을 μ œκ±°ν•˜λ©΄ λœλ‹€.

각 key point의 overlap λ˜λŠ” tallest building을 μ°ΎκΈ° μœ„ν•΄ building 배열을 μˆœνšŒν•œλ‹€: $O(n)$. λ”°λΌμ„œ $n$개 key pointλ₯Ό μ²˜λ¦¬ν•˜λŠ”λ° $O(n)$ 만큼의 연산이 ν•„μš”ν•˜λ―€λ‘œ, Brute Force 방식은 $O(n^2)$의 μ‹œκ°„λ³΅μž‘λ„λ₯Ό κ°–λŠ”λ‹€.

Brute Force 풀이
#include <bits/stdc++.h>

using namespace std;

#define MAX 100005
typedef pair<int, int> Coord;
typedef pair<pair<int, int>, int> Building;

vector<Building> blding;
vector<Coord> key_points; // collection of key points
vector<Coord> ret;

int main() {
  int N;
  scanf("%d", &N);

  for (int i = 0; i < N; i++) {
    int l, h, r;
    scanf("%d %d %d", &l, &h, &r);
    blding.push_back({ { l, r }, h });
    key_points.push_back({ l, h });
    key_points.push_back({ r, 0 });
  }

  sort(key_points.begin(), key_points.end());

  // process key points!
  for (Coord &point : key_points) {
    int x = point.first, y = point.second;
    int max_h = y;

    for (Building &building : blding) {
      int b_left = building.first.first, b_right = building.first.second;
      if (b_left > x || b_right <= x) continue;

      max_h = max(max_h, building.second);
    }

    ret.push_back({x, max_h});
  }

  // show intermediate result
  for (int i = 0; i < ret.size(); i++) {
    printf("(%d %d) ", ret[i].first, ret[i].second);
  }
  printf("\n");

  // process redundant key points
  // how? compare with behind
  vector<Coord> final_ret;
  final_ret.push_back(ret[0]);
  for (int i = 1; i < ret.size(); i++) {
    int prev_h = ret[i - 1].second;
    int curr_h = ret[i].second;

    if (prev_h != curr_h) {
      final_ret.push_back(ret[i]);
    }
  }

  // show final result
  for (int i = 0; i < final_ret.size(); i++) {
    printf("%d %d ", final_ret[i].first, final_ret[i].second);
  }

  return 0;
}

λ‹Ήμ—°νžˆ μ‹œκ°„μ΄ˆκ³Όκ°€ λ‚˜λ‹ˆ μ œμΆœν•˜μ§€ 말 것!


Divide and Conquer (feat. Merge Sort)

μ΄λ²ˆμ—λŠ” λ‹€λ₯Έ λ°©λ²•μœΌλ‘œ 이 문제λ₯Ό ν’€μ–΄λ³΄μž. 제λͺ©μ€ <Divide-and-Conquery>μ§€λ§Œ 핡심적인 μ•„μ΄λ””μ–΄λŠ” <Merge Sort>λ‹€.

일단 <Merge Sort>의 λ°©μ‹λŒ€λ‘œ 문제λ₯Ό μ ˆλ°˜μ”© λ‚˜λˆ„μ–΄ ν•΄κ²°ν•œ ν›„, κ·Έ κ²°κ³Όλ₯Ό <Merge>ν•  μ˜ˆμ •μ΄λ‹€. λ¨Όμ € μ ˆλ°˜μ”© λ‚˜λˆ„λŠ” 것뢀터 μ‚΄νŽ΄λ³΄λ©΄,

vector<Coord> skyline(vector<Building> buildings) {
  if (buildings.size() == 0) {
    return vector<Coord>();
  } else if (buildings.size() == 1) {
    int l = buildings[0].first.first, r = buildings[0].first.second, h = buildings[0].second;
    return vector<Coord>({ {l, h}, {r, 0} });
  } else {
    int mid = buildings.size() / 2;
    vector<Coord> left_side = skyline(vector<Building>(buildings.begin(), buildings.begin() + mid));
    vector<Coord> right_side = skyline(vector<Building>(buildings.begin() + mid, buildings.end()));
    return merge(left_side, right_side);
  }
}

μž…λ ₯받은 vector<Building>을 μ ˆλ°˜μ”© λ‚˜λˆ„μ–΄ κ²°κ³Όλ₯Ό λ„μΆœν•œ ν›„ merge()λ₯Ό 톡해 ν•©μΉœλ‹€. 자, 그럼 μ–΄λ–»κ²Œ 2개의 vector<Coord>λ₯Ό 적절히 ν•©μΉ˜λŠλƒκ°€ μ€‘μš”ν•΄μ‘Œλ‹€!! πŸ‘Š

2개의 skyline 배열을 mergeν•˜λŠ” 방법은 μ•„λž˜μ™€ κ°™λ‹€.

1. Let’s compare key points of skylines starting from the leftmost end.

2. For two key points of two skylines, choose the one skyline having key point with lesser x value.

3. If y value of chosen key point is less than the last seen height of other skyline. then update key point’s y to the last seen height of other skyilne.

4. Proceed to next key point of the chosen skyline.

5. Repeat [2-4] steps until both the skylines are completed.

6. Remove the redundant key points.

자! 그럼 μœ„μ˜ μ•Œκ³ λ¦¬μ¦˜μ„ λ‹¨κ³„λ³„λ‘œ μˆ˜ν–‰ν•΄λ³΄μž.

μ™€μš°!! λ†€λΌμš΄ λ°©λ²•μœΌλ‘œ 두 skyline을 merge ν–ˆλ‹€!! 😲 μ½”λ“œλ‘œ μž‘μ„±ν•˜λ©΄ μ•„λž˜μ™€ κ°™λ‹€.

vector<Coord> merge(vector<Coord> left_side, vector<Coord> right_side) {
  int i = 0, j = 0;
  int last_h1 = -1, last_h2 = -1;
  vector<Coord> ret;
  while (i < left_side.size() && j < right_side.size()) {
    Coord p1 = left_side[i], p2 = right_side[j];

    if (p1.first < p2.first) {
      ret.push_back({p1.first, max(p1.second, last_h2)});
      last_h1 = p1.second;
      i += 1;
    } else if (p1.first > p2.first) {
      ret.push_back({p2.first, max(p2.second, last_h1)});
      last_h2 = p2.second;
      j += 1;
    } else {
      // see later
    }
  }

  for (; i < left_side.size(); i++) {
    ret.push_back(left_side[i]);
  }
  for (; j < right_side.size(); j++) {
    ret.push_back(right_side[j]);
  }

  // process redundants
  vector<Coord> final_ret;
  final_ret.push_back(ret[0]);
  for (int i = 1; i < ret.size(); i++) {
    int prev_h = ret[i - 1].second;
    int curr_h = ret[i].second;

    if (prev_h != curr_h) {
      final_ret.push_back(ret[i]);
    }
  }

  return final_ret;
}

κ·ΈλŸ¬λ‚˜ μœ„μ˜ merge μ•Œκ³ λ¦¬μ¦˜μ—μ„œ μš°λ¦¬λŠ” 두 key point의 x 값이 같은 경우λ₯Ό 닀루지 μ•Šμ•˜λ‹€!

이 κ²½μš°λŠ” μ•„λž˜μ™€ 같이 ν•΄κ²°ν•œλ‹€.

If x values of both key points are same, then choose the one with higher y value and advance key points for both skylines.

κ·Έλž˜μ„œ μœ„μ˜ μ½”λ“œμ—μ„œ ν•΄λ‹Ή 뢀뢄을 μ±„μš°λ©΄ μ•„λž˜μ™€ κ°™λ‹€.

    if (p1.first < p2.first) {
      ret.push_back({p1.first, max(p1.second, last_h2)});
      last_h1 = p1.second;
      i += 1;
    } else if (p1.first > p2.first) {
      ret.push_back({p2.first, max(p2.second, last_h1)});
      last_h2 = p2.second;
      j += 1;
    } else {
      ret.push_back({p1.first, max(p1.second, p2.second)});
      last_h1 = p1.second;
      last_h2 = p2.second;
      i += 1;
      j += 1;
    }

μ΄κ²ƒμœΌλ‘œ <Skyline Problem>에 λŒ€ν•΄ μ‚΄νŽ΄λ³΄μ•˜λ‹€. ν•œ 가지 풀이법이 더 μžˆλ‹€κ³  ν•˜λŠ”λ°, <μš°μ„ μˆœμœ„ 큐>λ₯Ό μ΄μš©ν•˜λŠ” 방식이닀. 이 녀석은 λ‚˜μ€‘μ— μ‹œκ°„μ΄ λ‚˜λ©΄ μ •λ¦¬ν•˜λ„λ‘ ν•˜κ² λ‹€ πŸ˜‰


reference

Categories:

Updated: