-

프로그래머스 코딩테스트 연습 - 외벽 점검 (2020 카카오, 구현, 완전 탐색, 순열) 본문

프로그래머스 문제 풀이

프로그래머스 코딩테스트 연습 - 외벽 점검 (2020 카카오, 구현, 완전 탐색, 순열)

흣차 2022. 9. 21. 17:58
728x90
반응형

# 주소

https://school.programmers.co.kr/learn/courses/30/lessons/60062

 

프로그래머스

코드 중심의 개발자 채용. 스택 기반의 포지션 매칭. 프로그래머스의 개발자 맞춤형 프로필을 등록하고, 나와 기술 궁합이 잘 맞는 기업들을 매칭 받으세요.

programmers.co.kr

# 문제

레스토랑을 운영하고 있는 "스카피"는 레스토랑 내부가 너무 낡아 친구들과 함께 직접 리모델링 하기로 했습니다. 레스토랑이 있는 곳은 스노우타운으로 매우 추운 지역이어서 내부 공사를 하는 도중에 주기적으로 외벽의 상태를 점검해야 할 필요가 있습니다.

레스토랑의 구조는 완전히 동그란 모양이고 외벽의 총 둘레는 n미터이며, 외벽의 몇몇 지점은 추위가 심할 경우 손상될 수도 있는 취약한 지점들이 있습니다. 따라서 내부 공사 도중에도 외벽의 취약 지점들이 손상되지 않았는 지, 주기적으로 친구들을 보내서 점검을 하기로 했습니다. 다만, 빠른 공사 진행을 위해 점검 시간을 1시간으로 제한했습니다. 친구들이 1시간 동안 이동할 수 있는 거리는 제각각이기 때문에, 최소한의 친구들을 투입해 취약 지점을 점검하고 나머지 친구들은 내부 공사를 돕도록 하려고 합니다. 편의 상 레스토랑의 정북 방향 지점을 0으로 나타내며, 취약 지점의 위치는 정북 방향 지점으로부터 시계 방향으로 떨어진 거리로 나타냅니다. 또, 친구들은 출발 지점부터 시계, 혹은 반시계 방향으로 외벽을 따라서만 이동합니다.

외벽의 길이 n, 취약 지점의 위치가 담긴 배열 weak, 각 친구가 1시간 동안 이동할 수 있는 거리가 담긴 배열 dist가 매개변수로 주어질 때, 취약 지점을 점검하기 위해 보내야 하는 친구 수의 최소값을 return 하도록 solution 함수를 완성해주세요.

제한사항

  • n은 1 이상 200 이하인 자연수입니다.
  • weak의 길이는 1 이상 15 이하입니다.
    • 서로 다른 두 취약점의 위치가 같은 경우는 주어지지 않습니다.
    • 취약 지점의 위치는 오름차순으로 정렬되어 주어집니다.
    • weak의 원소는 0 이상 n - 1 이하인 정수입니다.
  • dist의 길이는 1 이상 8 이하입니다.
    • dist의 원소는 1 이상 100 이하인 자연수입니다.
  • 친구들을 모두 투입해도 취약 지점을 전부 점검할 수 없는 경우에는 -1을 return 해주세요.

입출력 예

nweakdistresult

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.Arrays;

class Solution {
    static int[] arr;
    static int[] temp;
    static int[] ans;
    static boolean[] visit;
    static int answer;
    public int solution(int n, int[] weak, int[] dist) {
        answer = Integer.MAX_VALUE;
        arr = weak;
        //1 3 4 5 13 15 16 17
        temp = new int[weak.length * 2];
        System.arraycopy(weak, 0, temp, 0, weak.length);
        for(int i = weak.length; i < temp.length; i++)
            temp[i] = weak[i - weak.length] + n;
        for(int i = 0; i < weak.length; i++) {
            visit = new boolean[dist.length];
            ans = new int[dist.length];
            makeArr(i);
            makeDist(0, dist.length, dist);
        }
        if(answer > dist.length)
            return -1;
        return answer;
    }
    static void makeArr(int count){
        System.arraycopy(temp, count, arr, 0, arr.length);
    }
    static void makeDist(int count, int end, int[] dist){
        if(count == end){
            int max = 0;
            int t = 0;
            int sum;
            for(int i = 0; i < ans.length; i++){
                if(t == arr.length)
                    break;
                sum = ans[i] + arr[t];
                while(t < arr.length && sum >= arr[t])
                    t++;
                max++;
            }
            if(t == arr.length)
                answer = Math.min(max, answer);
            return;
        }
        for(int i = 0; i < dist.length; i++){
            if(!visit[i]){
                visit[i] = true;
                ans[count] = dist[i];
                makeDist(count + 1, end, dist);
                ans[count] = 0;
                visit[i] = false;
            }
        }
    }
}

굉장히 난이도가 높은 문제입니다.

원형 탐색을 위해 배열을 계속해서 새로 생성하는 아이디어를 아셔야 풀 수 있겠습니다.

예를 들어 [1, 3, 4, 5]의 weak 배열에 n이 12라면 [1, 3, 4, 5]로도 만들 수 있어야 하고 [3, 4, 5, 13], [4, 5, 13, 15], [5, 13, 15, 16] 으로 만들어서 모든 배열을 그리디 방식으로 탐색을 하셔야 합니다.

무슨 말인지문제를 제대로 이해한 뒤 풀이를 해봅시다.

 

# 문제 해석

1. 원형 탐색을 하기 위해 배열을 [1, 3, 4, 5]의 weak 배열에 n이 12라면 [1, 3, 4, 5], [3, 4, 5, 13], [4, 5, 13, 15], [5, 13, 15, 16] 으로 만들 수 있어야 합니다.

2. dist배열을 순열을 이용하여 정렬한 뒤 그리디 방식으로 탐색합니다.

3. 예를 들어 [1, 3, 4, 5]의 weak배열에 dist배열이 [1, 2, 3]이라면 1에서 1칸, 2에서 2칸, 4에서 3칸가서 총 7까지 탐색 가능하므로 총 3이 정답이 되는 것입니다. 이것이 최소가 되는 dist배열은 3, 2또는 2, 3 또는 1, 3 등이 되겠습니다.

 

코드를 보며 이해해봅시다.

 

static int[] arr;
static int[] temp;
static int[] ans;
static boolean[] visit;

static 영역에서 사용할 배열은 다음과 같습니다.

arr는 weak배열을 순환하며 만들 배열이고 temp는 임시로 사용할 weak 배열에 n값을 더할 배열입니다.

ans는 dist배열을 순열로 만들 배열이며 visit은 dist의 중복 체크를 위한 배열입니다. 

이것이 어떻게 사용될지 한 번 보겠습니다.

answer = Integer.MAX_VALUE;
arr = weak;
//1 3 4 5 13 15 16 17
temp = new int[weak.length * 2];

정답이 될 answer는 최댓값으로 잡습니다.

arr는 최초에는 weak가 되며 temp는 weak배열의 2배 크기가 됩니다.

[1, 3, 5, 6]에 n이 10이면 [1, 3, 5, 6, 11, 13, 15, 16]이 temp배열이 됩니다.

이 중에서 맨 앞 4개, 그다음 4개, 그다음4개, 그 다음 4개 이렇게 연속된 것들을 arr배열로 만들 것입니다.

따라서

static void makeArr(int count){
    System.arraycopy(temp, count, arr, 0, arr.length);
}

다음과 같이 arrayCopy메서드를 이용하여 arr를 생성합니다.

System.arraycopy(weak, 0, temp, 0, weak.length);
for(int i = weak.length; i < temp.length; i++)
    temp[i] = weak[i - weak.length] + n;

또한 temp 배열의 생성은 간단하게 이 세 줄의 코드로 생성할 수 있습니다. weak배열에 n씩 각 배열 요소를 더한 것이 temp입니다.

for(int i = 0; i < weak.length; i++) {
    visit = new boolean[dist.length];
    ans = new int[dist.length];
    makeArr(i);
    makeDist(0, dist.length, dist);
}

이후 visit과 ans초기화를 하시고 mareArr를 이용하여 arr를 생성, makeDist를 이용하여 ans를 생성합시다.

makeDist는 다음과 같습니다.

for(int i = 0; i < dist.length; i++){
    if(!visit[i]){
        visit[i] = true;
        ans[count] = dist[i];
        makeDist(count + 1, end, dist);
        ans[count] = 0;
        visit[i] = false;
    }
}

boolean의 visit을 이용하여 순열을 만들 수 있습니다.

ans[count] = 0은 굳이 안해주어도 되지만 대칭성을 위해 넣었습니다. (안넣어도 정답됩니다. 초기화를 위해 한 것)

if(count == end){
    int max = 0;
    int t = 0;
    int sum;
    for(int i = 0; i < ans.length; i++){
        if(t == arr.length)
            break;
        sum = ans[i] + arr[t];
        while(t < arr.length && sum >= arr[t])
            t++;
        max++;
    }
    if(t == arr.length)
        answer = Math.min(max, answer);
    return;
}

이후 만든 arr배열과 ans배열을 이용하여 최소한으로 탐색할 수 있는 ans의 배열을 이용해 최소값을 구합니다.

if(answer > dist.length)
    return -1;
return answer;

이후 answer가 dist보다 크면 -1을, 아니면 answer를 출력하면 정답이 된답니다.

 

원형 탐색은 이렇게 신박하게 풀 수 있는걸 배웠습니다.

배열을 확장하여 순열, 그리디를 이용하여 푸는 문제였습니다.

카카오...쉽지않습니다

728x90
반응형
Comments