레스토랑을 운영하고 있는 "스카피"는 레스토랑 내부가 너무 낡아 친구들과 함께 직접 리모델링 하기로 했습니다. 레스토랑이 있는 곳은 스노우타운으로 매우 추운 지역이어서 내부 공사를 하는 도중에 주기적으로 외벽의 상태를 점검해야 할 필요가 있습니다.
레스토랑의 구조는 완전히 동그란 모양이고 외벽의 총 둘레는 n미터이며, 외벽의 몇몇 지점은 추위가 심할 경우 손상될 수도 있는 취약한 지점들이 있습니다. 따라서 내부 공사 도중에도 외벽의 취약 지점들이 손상되지 않았는 지, 주기적으로 친구들을 보내서 점검을 하기로 했습니다. 다만, 빠른 공사 진행을 위해 점검 시간을 1시간으로 제한했습니다. 친구들이 1시간 동안 이동할 수 있는 거리는 제각각이기 때문에, 최소한의 친구들을 투입해 취약 지점을 점검하고 나머지 친구들은 내부 공사를 돕도록 하려고 합니다. 편의 상 레스토랑의 정북 방향 지점을 0으로 나타내며, 취약 지점의 위치는 정북 방향 지점으로부터 시계 방향으로 떨어진 거리로 나타냅니다. 또, 친구들은 출발 지점부터 시계, 혹은 반시계 방향으로 외벽을 따라서만 이동합니다.
외벽의 길이 n, 취약 지점의 위치가 담긴 배열 weak, 각 친구가 1시간 동안 이동할 수 있는 거리가 담긴 배열 dist가 매개변수로 주어질 때, 취약 지점을 점검하기 위해 보내야 하는 친구 수의 최소값을 return 하도록 solution 함수를 완성해주세요.
제한사항
n | weak | dist | result |
---|---|---|---|
12 | [1, 5, 6, 10] | [1, 2, 3, 4] | 2 |
12 | [1, 3, 4, 9, 10] | [3, 5, 7] | 1 |
입출력 예에 대한 설명
입출력 예 #1
원형 레스토랑에서 외벽의 취약 지점의 위치는 다음과 같습니다.
친구들을 투입하는 예시 중 하나는 다음과 같습니다.
4m를 이동할 수 있는 친구는 10m 지점에서 출발해 시계방향으로 돌아 1m 위치에 있는 취약 지점에서 외벽 점검을 마칩니다.
2m를 이동할 수 있는 친구는 4.5m 지점에서 출발해 6.5m 지점에서 외벽 점검을 마칩니다.
그 외에 여러 방법들이 있지만, 두 명보다 적은 친구를 투입하는 방법은 없습니다. 따라서 친구를 최소 두 명 투입해야 합니다.
입출력 예 #2
원형 레스토랑에서 외벽의 취약 지점의 위치는 다음과 같습니다.
7m를 이동할 수 있는 친구가 4m 지점에서 출발해 반시계 방향으로 점검을 돌면 모든 취약 지점을 점검할 수 있습니다. 따라서 친구를 최소 한 명 투입하면 됩니다.
import java.util.ArrayList;
class Solution {
public static ArrayList<ArrayList<Integer>>distList;
public static int num;
public int solution(int n, int[] weak, int[] dist) {
// weak 를 원형이 아닌 일자로 나열하기 위해 2배로 늘림
ArrayList<Integer> weakList = new ArrayList<Integer>();
for (int i = 0; i < weak.length; i++) {
weakList.add(weak[i]);
}
for (int i = 0; i < weak.length; i++) {
weakList.add(weak[i] + n);
}
// 투입할 친구 수의 최솟값을 찾아야 하므로 len(dist) + 1로 초기화
int answer = dist.length + 1;
// 친구들을 선택할때의 순열을 작성(깊이를 조정해서 순열만들기)
distList = new ArrayList<ArrayList<Integer>>();
for(int i = 1; i <= dist.length; i++){
num = i; //친구를 선택하는 명수를 조정
backTracking(dist, 0, new boolean[dist.length], new int[dist.length]);
}
// 일열로 나열한 weak 반복
for(int start = 0; start <weak.length; start++){
//선택한 친구의 순열을 반복
for(int i = 0 ; i <distList.size(); i++){
int cnt = 1; //투입될 친구들의 수;
// 처음 친구가 진행할 수있는 작업의 길이(시작점 + dist)
int position = weakList.get(start) + distList.get(i).get(0);
// 총 weak 를 반복하면 되므로
for(int j = start+1; j < (weak.length + start); j++){
if(position < weakList.get(j)){
cnt += 1;
if (cnt > distList.get(i).size()) { // 더 투입이 불가능하다면 종료
break;
}
position = weakList.get(j) + distList.get(i).get(cnt-1);
}
}
if(position >= weakList.get(weak.length+start-1)) {
answer = Math.min(answer, cnt); // 최솟값 계산
}
}
}
if(answer > dist.length){
return -1;
}
return answer;
}
public void backTracking(int[] dist ,int depth ,boolean[] visited, int[] temp){
if(depth == num){
ArrayList<Integer>list = new ArrayList<>();
for(int i = 0; i <dist.length; i++) {
if(temp[i] !=0) {
list.add(temp[i]);
}
}
distList.add(list);
return;
}
for(int i = 0;i <dist.length; i++){
if(!visited[i]){
visited[i] = true;
temp[depth] = dist[i];
backTracking(dist, depth+1, visited,temp);
temp[depth] = 0;
visited[i] = false;
}
}
}
}
이문제는 당시 카카오코딩테스트중에서도 가장 어렵다고 불리는 문제이다. 문제를 보면 쉽게 구현할 수있을거같은데 바로 구현할 수가없었다. 그림을 아무리 그려봐도 어떻게해야하는지를 모르겠더라.... 그래서 여러 블로그들을 보고 나만의 방법으로 풀어보았다.
우선 카카오 공식풀이는 다음과 같다(링크 : https://tech.kakao.com/2019/10/02/kakao-blind-recruitment-2020-round1/) 문제의 의도를 보면
1. 원형으로 주어진 완전탐색 문제를 해결할 수 있는지 파악
2. bit mask나, permutation 등을 활용할 수 있는지 파악
이라고 되어있다.
우리는 답을보고 문제를 해결했지만 이문제를 해결함으로써 원형으로이루어진 완전탐색을 하는방법을 알게된 것이다.
좋게 생각하고 문제로 넘어가서 여러블로그에서 이차원배열, 배열변경 등등... 다양한 방법으로 풀었지만 나는 Arraylist에 weak.length * 2만큼 할당하여 n 까지는 원래값 그 다음부터는 n을 더한값을 넣어주어 모든방법을 확인하는 식의 방법을 구현했다.
또한 이문제는 순열을 사용하는 문제이다. 친구들을 선택하는 방법을 순열로 구해야하는데 백트레킹 알고리즘을 활용해 구현했다. 이순열방법이 다른 순열과 다른점음 depth를 1부터 dist.length까지 했다는점이다. 이 부분을 어떻게 구현해야할지 고민을 많이하고 헷갈려한거같다.
가장 머리가 아팠던 부분은 원형 완전탐색도 해결했고, 친구들을 선택하는 순열도 해결했는데 그래서 결국 어떻게 최소값을 구하는가? 였다.
방법은 우선 첫번째 for문에 weak만큼을 반복해준다. 두번째 for 문에서 순열수만큼 반복해준다. 세번째 for문에서 start 부터 start+ weak 까지 반복해서 선택한 친구들이 취약점을 다 정검할 수있는지 ,정검할 수있으면 최소 몇명으로 할 수있는지를 구해주면 된다.
방법을알아도 어려운 문제인데 실제로 코딩테스트에서 이문제를 본다면 손도못댈거같은 문제... 기죽지말고 해보자 !!