알고리즘/문제풀이

[백준] Q17144 미세먼지, 안녕! JAVA

케팔스 2022. 1. 18. 21:47

https://www.acmicpc.net/problem/17144

 

17144번: 미세먼지 안녕!

미세먼지를 제거하기 위해 구사과는 공기청정기를 설치하려고 한다. 공기청정기의 성능을 테스트하기 위해 구사과는 집을 크기가 R×C인 격자판으로 나타냈고, 1×1 크기의 칸으로 나눴다. 구사

www.acmicpc.net

 

1. 문제의 이해 및 유형

구현, simulation , 2차원 배열 순회(시계방향 테두리)

 

R $*$ C인 격자판에서 공기청정기 위치가 주어지고, T초 뒤의 상황을 구하는 문제이다.

문제에서 주어진 알고리즘 그대로 구현해내는 문제이다.

 

2. 문제  접근 방식

문제에 주어진 대로 구현하면 되는 것을 안 후에, 구현을 위해 필요한 것을 생각했다.1. 공기청정기의 위치. ([a,0], [b,0])2. 확산 및 공기청정기 작동 순서 고려하기.  2-1. 처음에 확산이 우선 되어야한다.    a. 확산되어야 하는 먼지의 위치를 어떻게 알까?       2차원 배열을 모두 보는 방법 또는 처음에 Queue에 모두 등록하는 방법    b. 확산을 할 때, 확산의 영향이 이후의 계산에 영향을 끼치면 안된다.    c. [5] [14] 에서 5 -> 14 순으로 계산할 때 고려하지 않는다면         [7][12]가 되는데, [4][1] + [2][12] => [6][13]이 올바른 값이다.    d. 영향을 주지 않도록 map을 하나 더 만들어 갱신하는 식으로 하면 될것 같다.  2-2. 확산된 map에서 공기청정기 위치를 활용하여 rotate한다.    2차원 배열 순회 방식.

 

작동 순서는 문제에 주어진 그대로 구현하되, 필요한 자료를 잘 정리하는 것에 중점을 두었다.

1. Queue에 먼지를 넣어두고, 꺼내면서 모든 먼지를 확산을 시킨다. (newMap에 갱신시키고 map에 update한다)

2. 이후 rotate를 한다.(이것도 당기는 방식 또는 미는 방식이 있을 수 있다, 순회 관점또는 접근 방법 차이)

3. 새로운 작업을 위해 map을 돌면서 새로운 먼지들을 queue에 등록한다.

/*
2022.01.07

이름 및 난이도 : 미세먼지 안녕! Gold IV samsung 
문제이해 
-----------------
 공기청정기는 항상 1번열
- 동작과정
    1. 미세먼지 확산 
        - 인접한 네 방향으로 확산
        - 확산되는 양은 A/5
        - 남은 먼지는 A - (A/5)* 확산된 방향
         ** 순서에 상관이 없어야 하지만 각각의 먼지가 분산되는 과정에서 영향을 줄 수 있다.
         ** 영향을 주지 않게 하기 위해 상태를 따로 저장해야한다.
           > 1-1 새로운 Map을 생성하여 반영하는 방법  >> 2차원 배열
           > 1-2 새로운 List를 생성하여 반영하는 방법. >> arrayList(but, 안에 많이 들어감)
    2. 공기청정기 작동
        - 위쪽은 반 시계 방향
        - 아래쪽은 시계방향.

접근 방법 :
제한 조건 : 
*/

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

public class Q17144_Review {
    static int R, C, T;
    static int[][] map;
    static int[] cleanerPosition;
    static ArrayList<Point> dustList;
    // dir 시계방향. ^ > V <
    static int[][] dir = new int[][] { { -1, 0 }, { 0, 1 }, { 1, 0 }, { 0, -1 } };

    public static void main(String[] args) throws IOException {
        input();
        solution();
    }

    public static void input() throws IOException {
        BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
        StringTokenizer st = new StringTokenizer(br.readLine());
        R = Integer.parseInt(st.nextToken());
        C = Integer.parseInt(st.nextToken());
        T = Integer.parseInt(st.nextToken());
        map = new int[R][C];
        cleanerPosition = new int[2]; // 0 위쪽 1 아래쪽
        dustList = new ArrayList<Point>();

        int idx = 0;
        for (int i = 0; i < R; i++) {
            st = new StringTokenizer(br.readLine());
            for (int j = 0; j < C; j++) {
                map[i][j] = Integer.parseInt(st.nextToken());
                if (j == 0 && map[i][j] == -1) {
                    cleanerPosition[idx++] = i;
                } else if (map[i][j] > 0) {
                    dustList.add(new Point(i, j));
                }
            }
        }
    }

    public static void solution() {
        while (T-- > 0) {
            dustDiffusion();
           // printMap();
            doCleaner();
            //printMap();
        }
        int ans = findAnswer();
        System.out.println(ans);
    }

    public static int findAnswer() {
        int ans = 0;
        for (Point p : dustList) {
            ans += map[p.x][p.y];
        }
        return ans;
    }

    public static void dustDiffusion() {
        // 영향을 배제하기 위한 새로운 맵 생성 (feat. clenar)
        int[][] newMap = new int[R][C];
        newMap[cleanerPosition[0]][0] = -1;
        newMap[cleanerPosition[1]][0] = -1;

        // 가지고 있는 dust들에 대해 확산시킴.
        for (Point cur : dustList) {
            // 5보다 낮으면 영향 X
            if (map[cur.x][cur.y] < 5) {
                newMap[cur.x][cur.y] += map[cur.x][cur.y];
                continue;
            }
            int cnt = 0;
            int diffuseSize = map[cur.x][cur.y] / 5;
            for (int i = 0; i < 4; i++) {
                int nx = dir[i][0] + cur.x;
                int ny = dir[i][1] + cur.y;
                if (nx < 0 || ny < 0 || nx > R - 1 || ny > C - 1)
                    continue;
                if (map[nx][ny] == -1)
                    continue;
                newMap[nx][ny] += diffuseSize;
                cnt++;
            }
            newMap[cur.x][cur.y] += map[cur.x][cur.y] - cnt * diffuseSize;
        }

        // map Update
        for (int i = 0; i < R; i++) {
            for (int j = 0; j < C; j++) {
                map[i][j] = newMap[i][j];
            }
        }
    }

    // 빨아들이는 느낌으로 간다. swap과 같은 과정이 필요 없기 때문.
    public static void doCleaner() {
        // 위방향
        int x = cleanerPosition[0] - 1;
        int y = 0;
        int d = 0;
        int offset = 1;
        int nx = -1;
        int ny = -1;
        while (true) {
            // 다음 칸 찾기.
            while (true) {
                nx = x + dir[d][0];
                ny = y + dir[d][1];
                if (nx < 0 || ny < 0 || nx > cleanerPosition[1] - 1 || ny > C - 1)
                    d = (d + offset) % 4;
                else
                    break;
            }
            if (map[nx][ny] == -1) {
                map[x][y] = 0;
                break;
            }
            map[x][y] = map[nx][ny];
            x = nx;
            y = ny;
        }

        // printMap();
        // 아래쪽 (아래 오른쪽 위쪽 왼쪽)
        x = cleanerPosition[1] + 1;
        y = 0;
        d = 2;
        offset = 3;

        while (true) {
            while (true) {
                nx = x + dir[d][0];
                ny = y + dir[d][1];
                if (nx < cleanerPosition[1] || ny < 0 || nx > R - 1 || ny > C - 1)
                    d = (d + offset) % 4;
                else
                    break;
            }
            if (map[nx][ny] == -1) {
                map[x][y] = 0;
                break;
            }
            map[x][y] = map[nx][ny];
            x = nx;
            y = ny;
        }
        // 새로운 미세먼지 Lis t생 성
        dustList = new ArrayList<Point>();
        for (int i = 0; i < R; i++) {
            for (int j = 0; j < C; j++) {
                if (map[i][j] > 0) {
                    dustList.add(new Point(i, j));
                }
            }
        }
    }

    public static void printMap() {
        System.out.println();
        for (int i = 0; i < R; i++) {
            for (int j = 0; j < C; j++) {
                System.out.print(map[i][j] + " ");
            }
            System.out.println();
        }
    }

    static class Point {
        int x, y;

        Point(int x, int y) {
            this.x = x;
            this.y = y;
        }
    }
}

 

3. 배운 점

2차원 배열 순회 방법

 - 여러 코드들을 보았을 때 따로따로 시계방향에 따라 범위를 주고 돌리는 방법(4개의 for문)

 - 이전에 달팽이 순회방법 코드를 본 것이 기억나 while문에서 방향만 바꾸어주는 방법 등을 사용하였다.

 

앞의 계산이 영향을 준다면 새로운 저장공간을 사용할 수 있다.