[프로그래머스] 카드 짝 맞추기 - JAVA

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

풀이

4 X 4 크기의 보드에 카드를 뒤집어 짝을 맞춰서 제거해야 한다.

카드 짝이 맞게 2개씩 위치를 저장해놓았다.

순열을 통해 제거할 카드 번호의 순서를 정해놓았다.

정해놓은 순서대로 제거해서 최소 이동을 계산하여 정답을 갱신했다.


메모리: 74.5MB

시간: 1.17ms

언어: Java 11

코드

import java.util.*;

class Solution {
    static class Node {
        int r;
        int c;
        int cnt;

        public Node(int r, int c, int cnt) {
            this.r = r;
            this.c = c;
            this.cnt = cnt;
        }
    }

    static Map<Integer, ArrayList<Node>> map;
    static ArrayList<Integer> list;
    static int sr, sc, answer;
    static int[][] board, boardCopy;

    static int[][] vector = { {1, 0}, {-1, 0}, {0, 1}, {0, -1} };

    public int solution(int[][] board, int r, int c) {
        sr = r;
        sc = c;
        this.board = board;

        map = new HashMap<>();
        list = new ArrayList<>();

        for(int i = 0; i < 4; i++) {
            for(int j = 0; j < 4; j++) {
                if(board[i][j] > 0) {
                    if(!map.containsKey(board[i][j])) {
                        map.put(board[i][j], new ArrayList<>());
                        list.add(board[i][j]);
                    }
                    map.get(board[i][j]).add(new Node(i, j, 0));
                }
            }
        }

        answer = Integer.MAX_VALUE;

        permutation(new int[list.size()], new boolean[list.size()], 0);

        return answer;
    }

    // 카드 선택 순서 정하기
    private static void permutation(int[] order, boolean[] visit, int idx) {
        if(idx == list.size()) {
            // 정해진 순서대로 제거해서 answer 갱신
            answer = Math.min(play(order), answer);
            return;
        }

        for(int i = 0; i < list.size(); i++) {
            if(visit[i]) {
                continue;
            }
            order[idx] = list.get(i);
            visit[i] = true;
            permutation(order, visit, idx + 1);
            visit[i] = false;
        }
    }

    // 진행
    private static int play(int[] order) {
        int r = sr;
        int c = sc;

        // 배열 복사해서 다시 원상복구 안해도 되도록
        boardCopy = new int[4][4];
        for(int i = 0; i < 4; i++) {
            boardCopy[i] = Arrays.copyOf(board[i], 4);
        }

        int tmp = 0;
        for(int i = 0; i < order.length; i++) {
            Node targetA = map.get(order[i]).get(0);
            Node targetB = map.get(order[i]).get(1);

            // 어느 쪽 카드부터 지울지 판단하기위해 거리 찾아서 비교
            int distA = getDist(r, c, targetA);
            int distB = getDist(r, c, targetB);

            // 두 거리 중 작은거 선택하고 다음 카드까지 거리 탐색, 현재 위치 갱신
            if(distA < distB) {
                tmp += distA + 1;
                tmp += getDist(targetA.r, targetA.c, targetB) + 1;
                r = targetB.r;
                c = targetB.c;
            }else {
                tmp += distB + 1;
                tmp += getDist(targetB.r, targetB.c, targetA) + 1;
                r = targetA.r;
                c = targetA.c;
            }

            boardCopy[targetA.r][targetA.c] = 0;
            boardCopy[targetB.r][targetB.c] = 0;
        }

        return tmp;
    }

    private static int getDist(int r, int c, Node target) {
        Queue<Node> queue = new LinkedList<>();
        boolean[][] visit = new boolean[4][4];

        queue.offer(new Node(r, c, 0));
        visit[r][c] = true;

        while(!queue.isEmpty()) {
            Node curr = queue.poll();

            if(curr.r == target.r && curr.c == target.c) {
                return curr.cnt;
            }

            for(int k = 0; k < 4; k++) {
                int nr = curr.r + vector[k][0];
                int nc = curr.c + vector[k][1];

                if(!isInBound(nr, nc)) {
                    continue;
                }

                // 한칸 이동해서 체크
                if(!visit[nr][nc]) {
                    queue.add(new Node(nr, nc, curr.cnt + 1));
                    visit[nr][nc] = true;
                }

                // 끝까지 가보기, 다른 카드 만나거나 끝 만날때까지
                while(isInBound(nr + vector[k][0], nc + vector[k][1])) {
                    if(boardCopy[nr][nc] != 0) {
                        break;
                    }
                    nr += vector[k][0];
                    nc += vector[k][1];
                }

                if(!visit[nr][nc]) {
                    queue.add(new Node(nr, nc, curr.cnt + 1));
                    visit[nr][nc] = true;
                }
            }

        }

        return 0;
    }

    private static boolean isInBound(int r, int c) {
        if(r < 0 || c < 0 || r >= 4 || c >= 4) {
            return false;
        }

        return true;
    }
}