사냥꾼 - 이분탐색으로(백준 8983번)

Run·2021년 8월 14일
0

TIL

목록 보기
2/8
post-thumbnail

문제

KOI 사냥터에는 N 마리의 동물들이 각각 특정한 위치에 살고 있다. 사냥터에 온 사냥꾼은 일직선 상에 위치한 M 개의 사대(총을 쏘는 장소)에서만 사격이 가능하다. 편의상, 일직선을 x-축이라 가정하고, 사대의 위치 x1, x2, ..., xM은 x-좌표 값이라고 하자. 각 동물이 사는 위치는 (a1, b1), (a2, b2), ..., (aN, bN)과 같이 x,y-좌표 값으로 표시하자. 동물의 위치를 나타내는 모든 좌표 값은 양의 정수이다.

사냥꾼이 가지고 있는 총의 사정거리가 L이라고 하면, 사냥꾼은 한 사대에서 거리가 L 보다 작거나 같은 위치의 동물들을 잡을 수 있다고 한다. 단, 사대의 위치 xi와 동물의 위치 (aj, bj) 간의 거리는 |xi-aj| + bj로 계산한다.

예를 들어, 아래의 그림과 같은 사냥터를 생각해 보자. (사대는 작은 사각형으로, 동물의 위치는 작은 원으로 표시되어 있다.) 사정거리 L이 4라고 하면, 점선으로 표시된 영역은 왼쪽에서 세 번째 사대에서 사냥이 가능한 영역이다.

사대의 위치와 동물들의 위치가 주어졌을 때, 잡을 수 있는 동물의 수를 출력하는 프로그램을 작성하시오.

입력

입력의 첫 줄에는 사대의 수 M (1 ≤ M ≤ 100,000), 동물의 수 N (1 ≤ N ≤ 100,000), 사정거리 L (1 ≤ L ≤ 1,000,000,000)이 빈칸을 사이에 두고 주어진다. 두 번째 줄에는 사대의 위치를 나타내는 M개의 x-좌표 값이 빈칸을 사이에 두고 양의 정수로 주어진다. 이후 N개의 각 줄에는 각 동물의 사는 위치를 나타내는 좌표 값이 x-좌표 값, y-좌표 값의 순서로 빈칸을 사이에 두고 양의 정수로 주어진다. 사대의 위치가 겹치는 경우는 없으며, 동물들의 위치가 겹치는 경우도 없다. 모든 좌표 값은 1,000,000,000보다 작거나 같은 양의 정수이다.

출력

출력은 단 한 줄이며, 잡을 수 있는 동물의 수를 음수가 아닌 정수로 출력한다.

풀이

이 문제를 처음 봤을 때 각각의 동물 위치를 기준으로 for문을 돌려 사정거리 안의 사대를 탐색하면 될 것이라 생각했다. 방법은 맞는 것 같지만 제한시간이 1초인 것으로 보아 시간초과 에러가 걸릴 것 같았다.
출제의도가 O(n) 걸리는 선형탐색이 아닌 O(nlogn) 걸리는 이진탐색을 사용하여 풀라는 것 같다.

Rough algorithm
동물의 위치를 기준으로 사격 가능한 범위를 찾고 이진탐색을 통해 그 범위 안에 있는 사대를 탐색한다.

사격 가능한 범위
위 그림을 보면 알듯이 최대 사정거리와 동물의 y좌표(최대 사정거리 이내)의 차의 두배가 해당 동물의 사격 가능 범위가 된다.

코드

import sys

gun_position_num , animal_num, gun_range = map(int, sys.stdin.readline().split())

gun_position_li = list(map(int, sys.stdin.readline().split()))
gun_position_li.sort()

cnt =0
for _ in range(animal_num):
  animal_x, animal_y = map(int,sys.stdin.readline().split())
  if animal_y > gun_range:
      continue
  else:
      gap = gun_range - animal_y
      least = animal_x -gap
      most = animal_x + gap
      left =0
      right = gun_position_num-1
      while left <= right:
          mid = (left + right)//2
          if  least<=gun_position_li[mid]<=most:
              cnt +=1
              break
          elif gun_position_li[mid]<least:
              left = mid+1
          elif gun_position_li[mid]> most:
              right = mid -1
print(cnt)

개인적인 고찰
암산이 느린 나한텐 변수명을 직관적으로 알아보게 만드는 게 중요한 듯
물론 다른 이유에서도 직관적인 변수명을 쓰는 게 좋겠지만
이진탐색 감을 잡은 것 같다 크크크크

profile
정글에서 살아남기

0개의 댓글