🧠 Algorithm/[JAVA] Programmers

[1835번] 단체사진 찍기

0_ch4n 2022. 6. 19. 20:36
반응형

2017 카카오코드 본선

 

✔️ 문제 설명

단체사진 찍기

가을을 맞아 카카오프렌즈는 단체로 소풍을 떠났다. 즐거운 시간을 보내고 마지막에 단체사진을 찍기 위해 카메라 앞에 일렬로 나란히 섰다. 그런데 각자가 원하는 배치가 모두 달라 어떤 순서로 설지 정하는데 시간이 오래 걸렸다. 네오는 프로도와 나란히 서기를 원했고, 튜브가 뿜은 불을 맞은 적이 있던 라이언은 튜브에게서 적어도 세 칸 이상 떨어져서 서기를 원했다. 사진을 찍고 나서 돌아오는 길에, 무지는 모두가 원하는 조건을 만족하면서도 다르게 서는 방법이 있지 않았을까 생각해보게 되었다. 각 프렌즈가 원하는 조건을 입력으로 받았을 때 모든 조건을 만족할 수 있도록 서는 경우의 수를 계산하는 프로그램을 작성해보자.

 

더보기

✔️ 입출력 예

첫 번째 예제는 문제에 설명된 바와 같이, 네오는 프로도와의 간격이 0이기를 원하고 라이언은 튜브와의 간격이 2보다 크기를 원하는 상황이다.

두 번째 예제는 무지가 콘과의 간격이 2보다 작기를 원하고, 반대로 콘은 무지와의 간격이 1보다 크기를 원하는 상황이다. 이는 동시에 만족할 수 없는 조건이므로 경우의 수는 0이다.

 

✔️ 입력 형식

입력은 조건의 개수를 나타내는 정수 n과 n개의 원소로 구성된 문자열 배열 data로 주어진다. data의 원소는 각 프렌즈가 원하는 조건이 N~F=0과 같은 형태의 문자열로 구성되어 있다. 제한조건은 아래와 같다.

  • 1 <= n <= 100
  • data의 원소는 다섯 글자로 구성된 문자열이다. 각 원소의 조건은 다음과 같다.
    • 첫 번째 글자와 세 번째 글자는 다음 8개 중 하나이다. {A, C, F, J, M, N, R, T} 각각 어피치, 콘, 프로도, 제이지, 무지, 네오, 라이언, 튜브를 의미한다. 첫 번째 글자는 조건을 제시한 프렌즈, 세 번째 글자는 상대방이다. 첫 번째 글자와 세 번째 글자는 항상 다르다.
    • 두 번째 글자는 항상 ~이다.
    • 네 번째 글자는 다음 3개 중 하나이다. {=, <, >} 각각 같음, 미만, 초과를 의미한다.
    • 다섯 번째 글자는 0 이상 6 이하의 정수의 문자형이며, 조건에 제시되는 간격을 의미한다. 이때 간격은 두 프렌즈 사이에 있는 다른 프렌즈의 수이다.

 

✔️ 출력 형식

모든 조건을 만족하는 경우의 수를 리턴한다.

 

✔️ 코드 구상

문제 처음보고 막막하다는 생각 밖에 안들었던거 같다..... 제일 눈에 들어왔던건 '경우의 수' 라는거뿐

최대한 간단하게 접근해보자!!! 라는 생각으로 우선 8명의 캐릭터가 설 수 있는 모든 경우의 수를 구한 다음에

거기서 조건이 모두 만족하는 경우만 카운트하면 되지 않을까?!! 로 시작하게 됐다.

 

✔️ 코드

  • 막상 해보니까 그렇게 어렵진 않았던거 같았다!!!
  • 모든 경우의 수를 List로 만드는 로직이 어려워서 구글링을 조금 참고했지만
  • 조건들을 판단하는 로직은 어렵지 않게 짤 수 있었다.
import java.util.*;

class Solution {
    public int solution(int n, String[] data) {
        int answer = 0;
        String characters = "ACFJMNRT";
        List<String> list = new ArrayList<>();

        perm("", characters, list);

        for(int i = 0; i < list.size(); i++) {
            int check = 0;

            for(int j = 0; j < data.length; j++) {
                if(check == 1) {
                    break;
                }

                int distance = Math.abs(list.get(i).indexOf(data[j].charAt(0)) - list.get(i).indexOf(data[j].charAt(2))) - 1;

                if(data[j].charAt(3) == '=') {
                    if(!(distance == Integer.parseInt(String.valueOf(data[j].charAt(4))))) {
                        check = 1;
                    }
                } else if(data[j].charAt(3) == '>') {
                    if(!(distance > Integer.parseInt(String.valueOf(data[j].charAt(4))))) {
                        check = 1;
                    }
                } else if(data[j].charAt(3) == '<') {
                    if(!(distance < Integer.parseInt(String.valueOf(data[j].charAt(4))))) {
                        check = 1;
                    }
                }
            }

            if(check == 0) {
                answer++;
            }
        }

        return answer;
    }

    public void perm(String uniqueCase, String characters, List<String> list) {
        int n = characters.length();

        if(n == 0) {
            list.add(uniqueCase);
            return;
        }

        for(int i = 0; i < n; i++) {
            perm(uniqueCase + characters.charAt(i),
                    characters.substring(0, i) + characters.substring(i + 1),
                    list);
        }
    }
}

 

✔️ 수정한 코드

  • 모든 경우의 수를 List에 추가하는 로직을 직접 짜보지 않은게 찝찝해서 직접 짜본 걸로 수정한 코드이다!
  • 재귀 호출을 이용해서 글자수 8개를 채울 때까지 모든 글자에 대한 트리를 뻗어가면서 visit으로 사용하지 않은 문자를 문자열에 추가하는 식으로 구현해봤다!
  • 우연히 실행 시간이 눈에 띄게 줄어서 뿌듯하다..!
import java.util.*;

class Solution {
    public int solution(int n, String[] data) {
        int answer = 0;
        String[] characters = {"A", "C", "F", "J", "M", "N", "R", "T"};
        List<String> list = new ArrayList<>();
        boolean[] visit = new boolean[characters.length];

        perm(visit, 0, "", characters, list);

        for(int i = 0; i < list.size(); i++) {
            int check = 0;

            for(int j = 0; j < data.length; j++) {
                if(check == 1) {
                    break;
                }

                int distance = Math.abs(list.get(i).indexOf(data[j].charAt(0)) - list.get(i).indexOf(data[j].charAt(2))) - 1;

                if(data[j].charAt(3) == '=') {
                    if(!(distance == Integer.parseInt(String.valueOf(data[j].charAt(4))))) {
                        check = 1;
                    }
                } else if(data[j].charAt(3) == '>') {
                    if(!(distance > Integer.parseInt(String.valueOf(data[j].charAt(4))))) {
                        check = 1;
                    }
                } else if(data[j].charAt(3) == '<') {
                    if(!(distance < Integer.parseInt(String.valueOf(data[j].charAt(4))))) {
                        check = 1;
                    }
                }
            }

            if(check == 0) {
                answer++;
            }
        }

        return answer;
    }

    public void perm(boolean[] visit, int depth, String makeCase, String[] characters, List<String> list) {
        if(depth == 8) {
            list.add(makeCase);
            return;
        }

        for(int i = 0; i < characters.length; i++) {
            if(visit[i] == false) {
                visit[i] = true;
                perm(visit, depth + 1, makeCase + characters[i], characters, list);
                visit[i] = false;
            }
        }
    }
}

 

📄 원문

https://programmers.co.kr/learn/courses/30/lessons/1835

반응형