1300번: K번째 수
세준이는 크기가 N×N인 배열 A를 만들었다. 배열에 들어있는 수 A[i][j] = i×j 이다. 이 수를 일차원 배열 B에 넣으면 B의 크기는 N×N이 된다. B를 오름차순 정렬했을 때, B[k]를 구해보자. 배열 A와 B
www.acmicpc.net
자세한 해설
단순하게 N*N 배열을 정의 한 후 B를 만들어서 정렬을 하면 시간초과가 나오기도 전에 메모리초과가 나옵니다.
이 문제를 풀기 위해서는 이진탐색을 활용해야 합니다.
이진탐색으로 B[k]를 찾는 과정을 크게보면 다음과 같습니다.
- 이진탐색으로 mid를 구합니다.
- mid가 B에서 몇번째 숫자인지 구합니다.
- 만약 mid가 k번째 수라면 index도 k라는 뜻이고, mid = B[k] 가 됩니다.
"mid가 B에서 몇번째 숫자인지"는 A의 각 행에서 "mid이하의 수"를 세면 됩니다.
다음은 N=3, k=7 의 예제입니다.
| 1 | 2 | 3 | 구구단 1단 |
| 2 | 4 | 6 | 구구단 2단 |
| 3 | 6 | 9 | 구구단 3단 |
i행이 구구단 i단의 형태이므로 mid // i 를 하면 i행의 "mid이하의 수"를 셀 수 있습니다.
1단부터 N단까지 "mid이하의 수"를 전부 세면 mid의 index가 됩니다.
이때 각 행에서 mid이하의 수 개수의 최대값은 N이므로 주의해서 구현합니다.
mid = (left+right)//2
index = 0
for i in range(1,N+1):
index += min(mid//i, N)
이제 mid의 index와 k를 비교합니다.
- index가 k 이상이면 mid를 저장합니다. 그리고 더 낮은 mid를 탐색합니다.
- index가 k 미만이면 더 높은 mid를 탐색합니다.
if index >= k:
answer = mid
right = mid - 1
else:
left = mid + 1
여기서 주의할점이 중간에 만족하는 mid와 index를 찾더라도 left가 right를 넘어갈 때 까지 계속 반복합니다.
만족하는 mid중 가능한 최소값을 찾기 위함입니다. 왜일까요?
다음은 예제에서 B[k]값과 k값 입니다.
| B[k] | k |
| 1 | 1 |
| 2 | 3 |
| 3 | 5 |
| 4 | 6 |
| 5 | 6 |
| 6 | 8 |
| 7 | 8 |
| 8 | 8 |
| 9 | 9 |
B[k]=6 이여도 k=8
B[k]=7 이여도 k=8
B[k]=8 이여도 k=8 입니다.
하지만 예제의 행렬을 보면 7이나 8이라는 B[k]는 있을 수 없습니다. 오직 최솟값인 6만이 B[k]로 존재합니다. 또한 k의 값도 7이 아닌 전부 8입니다. 중복되는 숫자가 많고 없는 숫자도 존재하기 때문입니다.
많은 부분에서 이런 현상이 일어나기 때문에 찾고자 하는 k와 index가 정확하게 일치하지 않더라도 만족하는 index의 최소값을 찾아야 합니다.
이 부분이 단순한 이분탐색이 아닌 매개 변수 탐색이 적용되는 이유입니다.
전체코드
N = int(input())
k = int(input())
left, right = 0, N*N
while left <= right:
mid = (left+right)//2 # B[k]
index = 0 # k
for i in range(1,N+1):
index += min(mid//i, N)
if index >= k:
answer = mid
right = mid - 1
else:
left = mid + 1
print(answer)
잡담
코드는 간단하지만 문제를 푸는 방향성과 구현하기 위해 index를 찾는 과정 자체가 생각하기 쉬운 문제는 아니였습니다. 문제풀이를 직접 올리면서 오래 해석해야 이해할 수 있었습니다.
매개 변수 탐색이 적용되는 여러 문제를 푸는대 많은 도움이 되었습니다.
'PS > 문제풀이' 카테고리의 다른 글
| programmers / 조이스틱 / python (0) | 2022.03.05 |
|---|---|
| programmers / 길 찾기 게임 / python (0) | 2022.03.05 |