HwangHub

[Java/Simulation] 백준 21611. 마법사상어와 블리자드 본문

workspace/알고리즘

[Java/Simulation] 백준 21611. 마법사상어와 블리자드

HwangJerry 2024. 3. 7. 12:11

🤔 Intuition

바라는 게 많은 시뮬레이션 문제. 길을 잃지 않도록 변수명을 잘 선언하고, 로직을 잘 구성한 뒤 코드를 작성해야 할듯.

  1. 탐색은 정직하게 달팽이탐색으로 구현할 것.
  2. pull 또는 stretch 과정에서 temp 배열 인덱스를 복잡하게 운영하는 것보다는 1차원으로 차곡차곡 쌓아두고, 이를 고대로 다시 달팽이탐색으로 복붙하는게 편할 듯
  3. 연속되는 구슬을 터뜨리기 위해 좌표를 저장하는 건, 나중에 길이를 체크하고 되돌아가서 하나하나 터뜨리는 논리이므로 stack 자료구조가 적절할 듯

🔎 Algorithm & Complexity

  • Algorithm : 시뮬레이션
  • 시간복잡도 : O(N^4) : N^2 탐색 X 최악의 경우 stack에 N*N개 채워진 상태로 뒤로감기 → 480 ms
  • 공간복잡도 : O(N^2) : 2차원 map 저장 + 최대 N^2 개를 저장하는 stack

👨🏻‍💻 Logic

메서드를 구현하여 시뮬레이션 흐름에 맞게 loop 태움

  1. 달팽이탐색 : 탐색 방향이 LEFT → DOWN → RIGHT → UP 이면서 해당 방향으로 탐색 개수가 1 1 2 2 3 3 4 4 … 흐름의 반복되는 패턴이므로 이를 이용하여 loop를 이용하여 구현함
  2. blizzard() : 재귀함수를 이용하여 d방향으로 s만큼 set map[y][x] = 0
  3. pull() : 배열에 공백이 없도록 상어 방향으로 당겨주는 메서드. 중력 구현처럼 map을 상어 위치부터 달팽이탐색 하면서 0이 아닌 값만 temp[] 배열에 차곡차곡 담아서, 이후 다시 달팽이탐색하면서 temp[] 값을 map에 붙여넣기
  4. bomb() : map을 달팽이탐색 하면서 연속된 구슬의 좌표를 stack에 차곡차곡 담으면서 진행. 연속된 구슬임을 판단하기 위해 구슬의 값을 peek이라는 변수에 담아둠.⇒ 한번이라도 터뜨린 적 있으면 return true / 없으면 return false 하도록 하여 pull & bomb 과정을 반복할 수 있게 함
  5. ⇒ 만약 연속되지 않는 구슬이 발견되면 stack의 size를 체크하여 4개 이상일 때 stack.pop()을 반복하면서 적재해 둔 좌표의 map 값을 0으로 set. || 만약 size가 4개 미만일 경우 stack을 그냥 비워줌. || 현재 구슬을 stack에 담고 달팽이탐색 재개
  6. stretch() : 반복되는 개수를 체크하는 cnt 변수와, 연속되는 구슬인지 여부를 판단하기 위한 peek 변수를 운영. 연속이 끊길 때마다 temp[] 값을 업데이트 하면서 진행. 연속된 구슬의 길이가 1개일 때에도 stretch가 발생하므로 temp에 대입할 때 index가 범위를 넘어가진 않는지 하나하나 체크할 필요 O

마지막으로... 코드가 반복되는 감이 없지 않지만 실전에서는 바로 떠오르지 않는 이상 그냥 이렇게 냅다 하는 편이다. 로직이 꼬이는 게 더 risky한 행동이기 때문이다. 스스로 논리가 이해가 된다면 우선 구현을 취한 뒤에, 문제가 없겠다고 판단이 선 이후에 + 시간이 남는다면 코드 최적화를 고려해볼 수 있겠다.

public class BOJ_21611_마법사상어와블리자드 {
    private static int N, M, tdx;
    private static int[][] map;
    private static int[] temp;
    private static int oneTotal, twoTotal, threeTotal;
    private static int shark;
    private static int[] dy = {0, -1, 1, 0, 0}; // 상하좌우 1,2,3,4 동치
    private static int[] dx = {0, 0, 0, -1, 1};
    private static final int UP = 1;
    private static final int DOWN = 2;
    private static final int LEFT = 3;
    private static final int RIGHT = 4;
    private static Deque<int[]> stack;
    public static void main(String[] args) throws IOException {
        BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
        BufferedWriter bw = new BufferedWriter(new OutputStreamWriter(System.out));
        StringBuilder sb = new StringBuilder();
        StringTokenizer st;

        st = new StringTokenizer(br.readLine());
        N = Integer.parseInt(st.nextToken());
        M = Integer.parseInt(st.nextToken());
        shark = (N+1) / 2;
        map = new int[N+1][N+1];
        
        for (int i = 1; i <= N; i++) {
            st = new StringTokenizer(br.readLine());
            for (int j = 1; j <= N; j++) {
                map[i][j] = Integer.parseInt(st.nextToken());
            }
        }

        for (int i = 0; i < M; i++) {
            st = new StringTokenizer(br.readLine());
            int d = Integer.parseInt(st.nextToken());
            int s = Integer.parseInt(st.nextToken());
            blizzard(d, s, 0, shark, shark);
            do {
                pull();
            } while (bomb());
            stretch();
        }

        sb.append(oneTotal + 2*twoTotal + 3*threeTotal);
        br.close();
        bw.write(sb.toString());
        bw.flush();
        bw.close();
    }

    private static boolean inRange(int y, int x) {
        return y >= 1 && y <= N && x >= 1 && x <= N;
    }
    private static void blizzard(int d, int s, int depth, int y, int x) {
        if (depth == s) {
            return;
        }
        int ny = y + dy[d];
        int nx = x + dx[d];

        if (inRange(ny, nx)) {
            // 터뜨림
            map[ny][nx] = 0;
            blizzard(d, s, depth + 1, ny, nx);
        }
    }

    private static boolean bomb() {
        stack = new ArrayDeque<>();
        int y = shark;
        int x = shark;
        int peek = -1;
        boolean isBomb = false;

        for (int i = 1; i <= N; i++) {
            for (int j = 1; j <= i; j++) {
                y += dy[LEFT];
                x += dx[LEFT];
                if (inRange(y, x)) {
                    if (peek == map[y][x]) {
                        stack.push(new int[]{y, x});
                    } else {
                        if (stack.size() >= 4) {
                            isBomb = true;
                            // 점수 계산
                            if (peek == 1) {
                                oneTotal += stack.size();
                            } else if (peek == 2) {
                                twoTotal += stack.size();
                            } else if (peek == 3) {
                                threeTotal += stack.size();
                            }

                            // 파괴
                            while (!stack.isEmpty()) {
                                int[] pos = stack.pop();
                                map[pos[0]][pos[1]] = 0;
                            }
                        } else {
                            // 4연속을 충족하지 못하는 구슬은 pass
                            stack = new ArrayDeque<>();
                        }
                        // 지금 꺼는 넣고 다시 시작
                        stack.push(new int[]{y, x});
                        peek = map[y][x];
                    }
                }
            }
            for (int j = 1; j <= i; j++) {
                y += dy[DOWN];
                x += dx[DOWN];
                if (inRange(y, x)) {
                    if (peek == map[y][x]) {
                        stack.push(new int[]{y, x});
                    } else {
                        if (stack.size() >= 4) {
                            isBomb = true;
                            // 점수 계산
                            if (peek == 1) {
                                oneTotal += stack.size();
                            } else if (peek == 2) {
                                twoTotal += stack.size();
                            } else if (peek == 3) {
                                threeTotal += stack.size();
                            }

                            // 파괴
                            while (!stack.isEmpty()) {
                                int[] pos = stack.pop();
                                map[pos[0]][pos[1]] = 0;
                            }
                        } else {
                            // 4연속을 충족하지 못하는 구슬은 pass
                            stack = new ArrayDeque<>();
                        }
                        // 지금 꺼는 넣고 다시 시작
                        stack.push(new int[]{y, x});
                        peek = map[y][x];
                    }
                }
            }
            i++;
            for (int j = 1; j <= i; j++) {
                y += dy[RIGHT];
                x += dx[RIGHT];
                if (inRange(y, x)) {
                    if (peek == map[y][x]) {
                        stack.push(new int[]{y, x});
                    } else {
                        if (stack.size() >= 4) {
                            isBomb = true;
                            // 점수 계산
                            if (peek == 1) {
                                oneTotal += stack.size();
                            } else if (peek == 2) {
                                twoTotal += stack.size();
                            } else if (peek == 3) {
                                threeTotal += stack.size();
                            }

                            // 파괴
                            while (!stack.isEmpty()) {
                                int[] pos = stack.pop();
                                map[pos[0]][pos[1]] = 0;
                            }
                        } else {
                            // 4연속을 충족하지 못하는 구슬은 pass
                            stack = new ArrayDeque<>();
                        }
                        // 지금 꺼는 넣고 다시 시작
                        stack.push(new int[]{y, x});
                        peek = map[y][x];
                    }
                }
            }
            for (int j = 1; j <= i; j++) {
                y += dy[UP];
                x += dx[UP];
                if (inRange(y, x)) {
                    if (peek == map[y][x]) {
                        stack.push(new int[]{y, x});
                    } else {
                        if (stack.size() >= 4) {
                            isBomb = true;
                            // 점수 계산
                            if (peek == 1) {
                                oneTotal += stack.size();
                            } else if (peek == 2) {
                                twoTotal += stack.size();
                            } else if (peek == 3) {
                                threeTotal += stack.size();
                            }

                            // 파괴
                            while (!stack.isEmpty()) {
                                int[] pos = stack.pop();
                                map[pos[0]][pos[1]] = 0;
                            }
                        } else {
                            // 4연속을 충족하지 못하는 구슬은 pass
                            stack = new ArrayDeque<>();
                        }
                        // 지금 꺼는 넣고 다시 시작
                        stack.push(new int[]{y, x});
                        peek = map[y][x];
                    }
                }
            }
        }
        return isBomb;
    }
    private static void pull() {
        temp = new int[N * N];
        
        copy();
        paste();
    }

    private static void paste() {
        tdx = 0;
        int y = shark;
        int x = shark;
        for (int i = 1; i <= N; i++) {
            for (int j = 1; j <= i; j++) {
                y += dy[LEFT];
                x += dx[LEFT];
                if (inRange(y, x)) {
                    map[y][x] = temp[tdx++];
                }
            }
            for (int j = 1; j <= i; j++) {
                y += dy[DOWN];
                x += dx[DOWN];
                if (inRange(y, x)) {
                    map[y][x] = temp[tdx++];
                }
            }
            i++;
            for (int j = 1; j <= i; j++) {
                y += dy[RIGHT];
                x += dx[RIGHT];
                if (inRange(y, x)) {
                    map[y][x] = temp[tdx++];
                }
            }
            for (int j = 1; j <= i; j++) {
                y += dy[UP];
                x += dx[UP];
                if (inRange(y, x)) {
                    map[y][x] = temp[tdx++];
                }
            }
        }
    }

    private static void copy() {
        tdx = 0;
        int y = shark;
        int x = shark;
        for (int i = 1; i <= N; i++) {
            for (int j = 1; j <= i; j++) {
                y += dy[LEFT];
                x += dx[LEFT];

                if (inRange(y, x) && map[y][x] > 0) {
                    temp[tdx++] = map[y][x];
                }
            }
            for (int j = 1; j <= i; j++) {
                y += dy[DOWN];
                x += dx[DOWN];

                if (inRange(y, x) && map[y][x] > 0) {
                    temp[tdx++] = map[y][x];
                }
            }
            i++; // 복사 길이 늘림 for 달팽이탐색
            for (int j = 1; j <= i; j++) {
                y += dy[RIGHT];
                x += dx[RIGHT];

                if (inRange(y, x) && map[y][x] > 0) {
                    temp[tdx++] = map[y][x];
                }
            }
            for (int j = 1; j <= i; j++) {
                y += dy[UP];
                x += dx[UP];

                if (inRange(y, x) && map[y][x] > 0) {
                    temp[tdx++] = map[y][x];
                }
            }
        }
    }
    private static void stretch() {
        tdx = 0;
        temp = new int[N * N];
        int peek = -1;
        int cnt = 0;

        int y = shark;
        int x = shark;
        for (int i = 1; i <= N; i++) {
            for (int j = 1; j <= i; j++) {
                y += dy[LEFT];
                x += dx[LEFT];
                if (inRange(y, x)) {
                    if (peek == map[y][x]) {
                        cnt++;
                    } else {
                        if (cnt > 0) {
                            if (tdx < N * N) {
                                temp[tdx++] = cnt;
                            }
                            if (tdx < N * N) {
                                temp[tdx++] = peek;
                            }
                        }
                        cnt = 0;
                        cnt++;
                        peek = map[y][x];
                    }
                }
            }
            for (int j = 1; j <= i; j++) {
                y += dy[DOWN];
                x += dx[DOWN];
                if (inRange(y, x)) {
                    if (peek == map[y][x]) {
                        cnt++;
                    } else {
                        if (cnt > 0) {
                            if (tdx < N * N) {
                                temp[tdx++] = cnt;
                            }
                            if (tdx < N * N) {
                                temp[tdx++] = peek;
                            }
                        }
                        cnt = 0;
                        cnt++;
                        peek = map[y][x];
                    }
                }
            }
            i++;
            for (int j = 1; j <= i; j++) {
                y += dy[RIGHT];
                x += dx[RIGHT];
                if (inRange(y, x)) {
                    if (peek == map[y][x]) {
                        cnt++;
                    } else {
                        if (cnt > 0) {
                            if (tdx < N * N) {
                                temp[tdx++] = cnt;
                            }
                            if (tdx < N * N) {
                                temp[tdx++] = peek;
                            }
                        }
                        cnt = 0;
                        cnt++;
                        peek = map[y][x];
                    }
                }
            }
            for (int j = 1; j <= i; j++) {
                y += dy[UP];
                x += dx[UP];
                if (inRange(y, x)) {
                    if (peek == map[y][x]) {
                        cnt++;
                    } else {
                        if (cnt > 0) {
                            if (tdx < N * N) {
                                temp[tdx++] = cnt;
                            }
                            if (tdx < N * N) {
                                temp[tdx++] = peek;
                            }
                        }
                        cnt = 0;
                        cnt++;
                        peek = map[y][x];
                    }
                }
            }
        }
        paste();
    }
}

 

 

Comments