본문 바로가기
Language/Java

[백준 | Java] 12425번 윷놀이(Small)

by ㅇ달빛천사ㅇ 2025. 2. 3.
728x90
12425번 / 윷놀이(Small)

윷놀이(Small)

🏷️ 관련 주제 : DFS Recursion Map



💦 나의 시도

먼저 입력값들을 하나씩 처리하였습니다.

 

던진 윷의 이동 칸 수를 저장할 때에는 큐 자료구조를 사용하였습니다.
먼저 Map에 Do ~ Mo를 키로 이동할 칸 수를 값으로 하여 저장 후,
입력값을 처리할 때 각 키 값으로 이동 칸 수를 조회하여 Queue<Integer> q에 담아주었습니다.

 

그리고 A팀과 B팀의 말의 위치를 담을 자료구조로는 처음에 이차원 int 배열을 사용하였으나
DFS 탐색 시, 이차원 배열에서 현재 말의 위치를 반복문을 통해 찾는 것은 Map이 O(1)으로 더 빠르므로

최종적으로 Map<Integer, Integer> 배열을 사용하여 각 인덱스에 해당하는 Map에 말의 위치를 키로 저장해 주었습니다.
(A팀 : 0번 인덱스, B팀 : 1번 인덱스)


용이가 복구한 말의 위치는 인스턴스 변수로 target변수에 저장하였습니다.

  • 키 : 각 팀의 말 위치 번호
  • 값 : 1 (말의 수는 체크하지 않으므로 임의의 값으로 지정)

그리고 DFS 탐색 시, 말을 이동하기 위해 Map<Integer, Integer> 배열 map을 만들었습니다.

  • 0번 인덱스의 Map : A 팀의 말 위치 및 수 저장
  • 1번 인덱스의 Map : B 팀의 말 위치 및 수 저장
  • Map의 Key-Value
    • 키 : 말의 위치
    • 값 : 해당 위치에 말의 수
    • -1 키 값에 아직 출발하지 않은 말의 수 저장

 

입력값 처리를 하고 나니

말 이동, 다음 턴 구하기, 말 잡기, 업기 등 다양한 규칙으로 한번에 쉽게 코드가 써지지는 않습니다.


그래서 먼저 주석으로 제가 처리해야할 동작들을 정리해 보았습니다.

그리고 해당 동작들을 메서드로 하나씩 만들어 보았습니다.

 

먼저 말의 시작 위치 start와 이동 칸 수 move를 매개 변수로

말이 도착할 위치 번호를 반환하는 메서드 moveHorse()를 만들었습니다.

 

다음으로 매개변수로 모든 팀의 말의 위치가 담긴 Map<Integer, Integer> 배열 map을 주면

용이가 정리한 말의 위치가 담긴 인스턴스 변수 target과 키를 비교하여

그 결과를 boolean형으로 반환하는 sameArrange() 메서드를 만들었습니다.

 

마지막으로 DFS를 하기 위해

모든 팀의 말의 위치 Map<Integer, Integer> 배열 map, 현재 팀의 순서 int team, 현재 이동할 말의 위치 int curIdx, 던진 윷의 정보가 담긴 Queue<Integer> moves를 매개변수로 받아

다음 팀의 순서(A팀: 0, B팀: 1) 혹은 게임이 끝났거나 moves가 비었을 때, 용이가 정리한 말의 위치와 현재 탐색 결과 말의 위치를 비교한 결과(일치 : 2, 불일치 : -1)를 반환하는 메서드 arrangeHorse() 메서드를 만들었습니다.

 

그리고 main 메서드에서 arrangeHorse()메서드를 호출하였습니다.
A팀부터 시작이므로 team0,

아직 출발한 말들이 없으므로 curIdx-1로 하여 해당 메서드를 호출하고
그 결과를 int result에 담아

result2이면 BufferedWriter"Case #" + i + ": YES\n"를 쓰고
-1이면 "Case #" + i + ": NO\n"를 쓰도록 하였습니다.


그리고 모든 테스트 케이스가 종료되면 BufferedWriter를 종료하여 모든 문자열을 출력하도록 하였습니다.

❌ 틀렸습니다.

50% 정도까지 통과되다가 '틀렸습니다.'가 뜨는데 반례를 쉽게 찾지 못하여 결국 해결하지 못하였습니다.
질문 게시판에도 질문이 하나도 없어서 크게 도움 받지 못하였는데
틀린 이유가 뭘까요?

 

 

설이 끝나고 아직 문제를 포기할 수 없었습니다. 다시 틀린 이유를 찾기 위해 코드를 다시 읽어보았습니다.

불필요한 조건부가 보여 코드를 수정하다 moveHorse() 메서드가 조금 이상한 것을 발견하였습니다.
10 - 25 - 26 - 22 - 27 -28 - 0으로 말이 지나가는 부분의 가운데가 22인데 그냥 (24 + move)로 계산한 부분이 있어서
moveHorse()메서드를 수정하기 시작하였습니다.


윷판을 다시 만들어 볼까? 고민하였습니다.

이걸 어떻게 구현하지 생각하며 LinkedList도 찾아보고 했는데
그냥 기존 메서드의 switch-case문에서 결과값을 바로 int형으로 반환하였다면
수정한 코드에서는 지름길로 빠질 수 있는 위치에서는 재귀함수를 호출하도록 수정하였습니다.


기존 moveHorse() 메서드
<pre class="java"><code>public static int moveHorse(int start, int move) {
      switch (start) {
          case 0 : return 30;
          case -1 : return move;
          case 5 : return 19 + move;
          case 10 : return 24 + move;
          case 22 : {
              int nextIdx = 26 + move;

              if (nextIdx > 29) {
                  return 30;
              }

              return nextIdx % 29;
          }
          default : {
              int nextIdx = start + move;
              if (start >= 20 && start <= 24 && nextIdx > 24) {
                  return start + move - 10;
              }

              if (start >= 15 && start <= 19) {
                  if (nextIdx > 20) {
                      return 30;
                  }
                  return nextIdx % 29;
              }

              return (nextIdx > 29) ? 30 : nextIdx % 29;
          }
      }
  }</code></pre>

맞았습니다!!

  • 메모리 : 15176 KB
  • 시간 : 136 ms
  • 코드 길이 : 5695 B


📑제출 기록 및 오답 원인


💯 해결 방법

import java.io.*;
import java.util.*;

public class Main {
    public static Map<Integer, Integer>[] target;
    public static int U;
    public static int N;
    public static int A;
    public static int B;

    public static void main(String[] args) throws IOException {
        BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
        BufferedWriter bw = new BufferedWriter(new OutputStreamWriter(System.out));
        int T = Integer.parseInt(br.readLine());

        Map<String, Integer> move = new HashMap<>(Map.of(
            "Do", 1,
            "Gae", 2,
            "Gul", 3,
            "Yut", 4,
            "Mo", 5
        ));

        for (int i = 1; i <= T; i++) {
            StringTokenizer st = new StringTokenizer(br.readLine());
            U = Integer.parseInt(st.nextToken());  // 한 팀에서 사용가능한 말의 수
            N = Integer.parseInt(st.nextToken());  // 던져진 윷의 목록 개수
            A = Integer.parseInt(st.nextToken());  // 판 위에 놓여 있는 A팀 말의 개수
            B = Integer.parseInt(st.nextToken());  // 판 위에 놓여 있는 B팀 말의 개수

            Queue<Integer> q = new LinkedList<>();

            st = new StringTokenizer(br.readLine());

            for (int j = 0; j < N; j++) {
                q.offer(move.get(st.nextToken()));
            }

            target = new HashMap[2];
            target[0] = new HashMap<Integer, Integer>();
            target[1] = new HashMap<Integer, Integer>();

            for (int k = 0; k < 2; k++) {
                st = new StringTokenizer(br.readLine());

                int bound = (k == 0) ? A : B;

                for (int j = 0; j < bound; j++) {
                    target[k].put(Integer.parseInt(st.nextToken()), 1);
                }
            }

            Map<Integer, Integer>[] map = new HashMap[2];  // 29 : 한 바퀴 돌아 0, 30 : 완주, 31 : 시작 전
            map[0] = new HashMap<>(Map.of(-1, U));
            map[1] = new HashMap<>(Map.of(-1, U));

            int result = arrangeHorse(map, 0, -1, q);

            switch (result) {
                case 2 : {
                    bw.write("Case #" + i + ": YES\n");
                    break;
                }
                case -1 : {
                    bw.write("Case #" + i + ": NO\n");
                    break;
                }
                default : bw.write("Case #" + i + ": NO\n");
            }
        }

        bw.close();
    }

    public static int arrangeHorse(Map<Integer, Integer>[] map, int team, int curIdx, Queue<Integer> moves) {
        int move = moves.poll();
        int nextIdx = moveHorse(curIdx, move);

        if (nextIdx == 30) {
            map[team].remove(curIdx);

            if (moves.isEmpty() && sameArrange(map)) {
                return 2;
            }

            return -1;
        }

        int nextTeam = (move >= 4) ? team : 1 - team;
        int curCnt = 1;

        if (curIdx == -1) {
            if (!map[team].containsKey(curIdx)) {
                return -1;
            }

            if (map[team].get(curIdx) == 1) {
                map[team].remove(curIdx);
            } else {
                map[team].put(-1, map[team].get(-1) - 1);
            }
        } else {
            curCnt = map[team].get(curIdx);
            map[team].remove(curIdx);
        }

        map[team].put(nextIdx, map[team].getOrDefault(nextIdx, 0) + curCnt);

        if (map[1 - team].containsKey(nextIdx)) {
            map[1 - team].put(-1, map[1 - team].getOrDefault(-1, 0) + map[1 - team].get(nextIdx));
            map[1 - team].remove(nextIdx);
            nextTeam = team;
        }

        if (moves.isEmpty()) {
            if (sameArrange(map)) {
                return 2;
            }
            return -1;
        }

        Set<Integer> horses = map[nextTeam].keySet();

        for (int horse : horses) {
            Queue<Integer> copyMoves = new LinkedList<>(moves);
            Map<Integer, Integer>[] copyMap = new HashMap[2];
            copyMap[0] = new HashMap<>(map[0]);
            copyMap[1] = new HashMap<>(map[1]);
            int nextTeam2 = arrangeHorse(copyMap, nextTeam, horse, copyMoves);

            if (nextTeam2 == 2) {
                return 2;
            }
        }

        return -1;
    }

    public static int moveHorse(int start, int move) {
        if (move == 0) {
            return start;
        }

        switch (start) {
            case -1 : return move;
            case 0 : return 30;
            case 5 : return moveHorse(20, move - 1);
            case 10 : return moveHorse(25, move - 1);
            case 22 : return moveHorse(27, move - 1);
            case 25 : return moveHorse(26, move - 1);
            case 26 : return moveHorse(22, move - 1);
            case 27 : return moveHorse(28, move - 1);
            case 28 : return moveHorse(0, move - 1);
        }

        int nextIdx = start + move;

        if (start >= 20 && start <= 24 && nextIdx > 24) {
            return nextIdx - 10;
        }

        if (start >= 15 && start <= 19) {
            if (nextIdx > 20) {
                return 30;
            }

            return nextIdx % 20;
        }

        return nextIdx;
    }


    public static boolean sameArrange(Map<Integer, Integer>[] map) {
        for (int i = 0; i < 2; i++) {
            Set<Integer> keys = new HashSet(map[i].keySet());
            keys.remove(-1);
            Set<Integer> keysTarget = target[i].keySet();

            if (!keys.equals(keysTarget)) {
                return false;
            }
        }

        return true;
    }
}

🏷️ 문제 풀면서 참고한 블로그

728x90


Top