https://programmers.co.kr/learn/courses/30/lessons/64062
문제 설명
[본 문제는 정확성과 효율성 테스트 각각 점수가 있는 문제입니다.]
카카오 초등학교의 니니즈 친구들이 라이언 선생님과 함께 가을 소풍을 가는 중에 징검다리가 있는 개울을 만나서 건너편으로 건너려고 합니다. 라이언 선생님은 니니즈 친구들이 무사히 징검다리를 건널 수 있도록 다음과 같이 규칙을 만들었습니다.
- 징검다리는 일렬로 놓여 있고 각 징검다리의 디딤돌에는 모두 숫자가 적혀 있으며 디딤돌의 숫자는 한 번 밟을 때마다 1씩 줄어듭니다.
- 디딤돌의 숫자가 0이 되면 더 이상 밟을 수 없으며 이때는 그 다음 디딤돌로 한번에 여러 칸을 건너 뛸 수 있습니다.
- 단, 다음으로 밟을 수 있는 디딤돌이 여러 개인 경우 무조건 가장 가까운 디딤돌로만 건너뛸 수 있습니다.
니니즈 친구들은 개울의 왼쪽에 있으며, 개울의 오른쪽 건너편에 도착해야 징검다리를 건넌 것으로 인정합니다.
니니즈 친구들은 한 번에 한 명씩 징검다리를 건너야 하며, 한 친구가 징검다리를 모두 건넌 후에 그 다음 친구가 건너기 시작합니다.
디딤돌에 적힌 숫자가 순서대로 담긴 배열 stones와 한 번에 건너뛸 수 있는 디딤돌의 최대 칸수 k가 매개변수로 주어질 때, 최대 몇 명까지 징검다리를 건널 수 있는지 return 하도록 solution 함수를 완성해주세요.
[제한사항]
- 징검다리를 건너야 하는 니니즈 친구들의 수는 무제한 이라고 간주합니다.
- stones 배열의 크기는 1 이상 200,000 이하입니다.
- stones 배열 각 원소들의 값은 1 이상 200,000,000 이하인 자연수입니다.
- k는 1 이상 stones의 길이 이하인 자연수입니다.
[입출력 예]
stones | k | result |
[2, 4, 5, 3, 2, 1, 4, 2, 5, 1] | 3 | 3 |
풀이
효율성 테스트가 없었다면 각 원소들을 1씩 차감시켜 연속되는 0이 k번 이상인 경우를 체크해줘서 쉽게 풀이가 가능하다.
하지만 stones 배열의 원소는 최대 200,000,000까지 될 수 있기 때문에 이 방법을 이용하게되면 최대 200,000,000번 즉, O(N)만큼의 시간 복잡도가 걸리기 때문에 효율성 테스트를 통과할 수 없다.
0부터 N까지 차례로 올라가며 다리를 건널 수 있는지 파악하는 방법보다 더 빠르게 값을 찾아내기 위해 이진탐색을 사용했다. 이진 탐색의 경우 O(logN)으로 일반적인 for문에 의한 탐색보다 더 효율적으로 탐색이 가능하다.
시작값은 0, 끝값은 stones 배열의 원소중 최대값으로 둔다.
시작값과 끝값의 중간 값 mid를 구하고 mid만큼 니니즈 친구들이 지나간다고 할 때
이 때 건너는 것이 가능하다면 mid 이상의 더 많은 친구들이 건널 수 있기 때문에 start = mid + 1로 변경하고 다시 탐색을 시작한다.
반대로 불가능하다면 mid 보다 적은 수의 친구들이 건너갈 수 있는 것이기 때문에 end = mid - 1로 변경하고 다시 탐색을 시작한다.
탐색은 start가 end 보다 커질 때까지, 즉 두 지점이 만나지 않을 때까지 진행하고, 만나는 지점이 답이 된다.
코드
public class Solution {
private int limit;
private int answer;
public int solution(int[] stones, int k) {
limit = k;
int max = Integer.MIN_VALUE;
for (int stone : stones) {
max = Math.max(max, stone);
}
binarySearch(stones, 0, max);
return answer;
}
private void binarySearch(int[] stones, int start, int end) {
while (start <= end) {
int mid = (start + end) / 2;
int[] cloneStones = setStones(stones.clone(), mid);
if (isPossible(cloneStones)) {
start = mid + 1;
} else {
end = mid - 1;
}
}
answer = start;
}
private boolean isPossible(int[] stones) {
int count = 0;
for (int i = 0; i < stones.length; i++) {
if (stones[i] == 0) {
count++;
} else {
count = 0;
continue;
}
if (count == limit) {
return false;
}
}
return true;
}
private int[] setStones(int[] stones, int value) {
for (int i = 0; i < stones.length; i++) {
if (stones[i] <= value) {
stones[i] = 0;
}
}
return stones;
}
}
'Tech > Problem Solving' 카테고리의 다른 글
[백준 15591] MooTube (Silver) (Java) (0) | 2020.11.02 |
---|---|
[프로그래머스] 카카오 블라인드 리쿠르트 2020 - 블록 이동하기 (Java) (0) | 2020.09.02 |
[프로그래머스] 카카오 블라인드 리크루트 2020 - 외벽 점검 (Java) (0) | 2020.08.31 |
[백준 3197] 백조의 호수 (Java) (1) | 2020.08.19 |
[백준 1665] 가운데를 말해요 (Java) (0) | 2020.08.18 |