2020. 4. 11. 16:51ㆍ알고리즘/PS
문제
https://www.acmicpc.net/problem/17144
17144번: 미세먼지 안녕!
미세먼지를 제거하기 위해 구사과는 공기청정기를 설치하려고 한다. 공기청정기의 성능을 테스트하기 위해 구사과는 집을 크기가 R×C인 격자판으로 나타냈고, 1×1 크기의 칸으로 나눴다. 구사과는 뛰어난 코딩 실력을 이용해 각 칸 (r, c)에 있는 미세먼지의 양을 실시간으로 모니터링하는 시스템을 개발했다. (r, c)는 r행 c열을 의미한다. 공기청정기는 항상 1번 열에 설치되어 있고, 크기는 두 행을 차지한다. 공기청정기가 설치되어 있지 않은 칸에는 미세먼
www.acmicpc.net

해설
단순구현 시뮬레이션 문제입니다. 문제의 조건을 간단히 정리하자면 아래와 같습니다.
- 공기청정기는 첫번째 열에서 2행을 차지하며 가장 윗 행과 아랫 행에서 두 칸 이상 떨어져있다.
- 공기청정기의 윗부분은 반시계 방향으로 아랫부분은 시계방향으로 순환하며 먼지를 한 칸씩 민다.
- 미세먼지는 매 초마다 위, 아래, 왼쪽, 오른쪽으로 확산된다.
- 특정 좌표에서의 미세먼지의 크기가 A라고 했을 때 확산되는 미세먼지는 A / 5이고 해당 좌표에서는 확산된 미세먼지의 크기 만큼 미세먼지의 크기가 줄어든다.
- 공기청정기에 들어간 미세먼지는 정화되서 사라진다.
여기서 처리해야할 동작은 미세먼지와 공기청정기 동작으로 나눌 수 있는데 각각 다음과 같은 사항을 고려해서 구현해야합니다.
미세먼지
- 미세먼지는 공기청정기가 있는 곳에는 확산되지 못한다.
- 미세먼지는 기존에 미세먼지가 있는 곳으로 확산되어 지지만 미세먼지는 매 초에 동시에 확산되어지므로 이를 기준으로 미세먼지의 양을 갱신할 때 주의해야 한다.
공기청정기
- 공기 청정기에서 나오는 바람의 경로의 미세먼지들을 한 칸씩 밀어야하며, 공기청정기에 들어간 먼지는 사라진다.
- 바람은 순환방향으로 이동한다.
- 공기청정기는 상/하로 나누어져 있다.
코드를 보며 설명을 이어가도록 하겠습니다.
구현
import java.util.*; public class Main { static StringBuilder sb = new StringBuilder(); // 행 r, 열 c, 시간 t static int r, c, t; static int[][] map; // 공기청정기의 행 좌표를 저장할 배열, 공기청정기의 열좌표는 고정이다. static int[] cr; static int[] dx = {-1, 0, 1, 0}; static int[] dy = {0, 1, 0, -1}; static void dust() { // 각 미세먼지의 확산을 기록할 배열 int[][] spread = new int[r][c]; for(int i = 0; i<r; i++) { for(int j = 0; j<c; j++) { // 미세먼지를 발견하면 if(map[i][j] > 0) { // 확산될 양을 기록한다. int div = map[i][j] / 5; for(int dir = 0; dir<4; dir++) { int nx = i + dx[dir]; int ny = j + dy[dir]; // 확산될 좌표가 유효한 범위 내에 존재하는지, 확산될 곳이 공기청정기인지 검사한다. if(nx < 0 || nx >= r || ny < 0 || ny >= c || map[nx][ny] == -1) continue; // 두가지 경우가 아니라면 확산을 기록할 배열에 확산된 좌표에 확산 양만큼 // 더하여 기록한다. spread[nx][ny] += div; // 기존의 미세먼지는 확산된 만큼 크기가 감소한다. map[i][j] -= div; } } } } // 확산된 미세먼지만큼 각 좌표를 갱신한다. for(int i = 0; i<r; i++) { for(int j = 0; j<c; j++) { map[i][j] += spread[i][j]; } } } static void cleaner() { // 탐색방향은 공기청정기 순환방향의 역방향 // 공기청정기의 윗부분은 반시계 방향이므로 시계방향으로 탐색 int udir = 0; // 공기청정기 윗부분의 좌표 int ur = cr[0]; int uc = 0; while(true) { // 탐색할 좌표 int nx = ur + dx[udir]; int ny = uc + dy[udir]; // 탐색할 좌표가 다시 공기청정기 이면 한바퀴 다 돈 것이므로 종료 if(nx == cr[0] && ny == 0) break; // 만약 탐색할 좌표가 범위를 벗어났으면 방향을 바꾸어준다. // 시계방향이므로 방향은 위 -> 오른쪽 -> 아래 -> 왼쪽 순서이다. if(nx < 0 || nx >= cr[1] || ny < 0 || ny >= c) { udir = (udir + 1) % 4; }else { // 해당 좌표에 미세먼지가 존재하지 않으면 넘어간다. if(map[nx][ny] != 0) { // 미세 먼지가 들어갈 곳인 이전좌표 (ur, uc)가 공기청정기이면 if(ur == cr[0] && uc == 0) { // 미세먼지를 제거한다. map[nx][ny] = 0; }else { // 해당 미세먼지는 이전 좌표로 이동하고 // 해당 좌표의 미세먼지는 이동했으므로 0으로 초기화한다. map[ur][uc] = map[nx][ny]; map[nx][ny] = 0; } } // 이전 좌표를 현재 좌표로 갱신합니다. ur = nx; uc = ny; } } // 탐색방향은 공기청정기 순환방향의 역방향 // 공기청정기의 아랫부분은 시계 방향이므로 반시계방향으로 탐색 int ddir = 2; // 공기청정기의 아랫부분 좌표 int dr = cr[1]; int dc = 0; while(true) { // 탐색할 좌표 int nx = dr + dx[ddir]; int ny = dc + dy[ddir]; // 탐색할 좌표가 다시 공기청정기 이면 한바퀴 다 돈 것이므로 종료 if(nx == cr[1] && ny == 0) break; // 만약 탐색할 좌표가 범위를 벗어났으면 방향을 바꾸어준다. // 반시계방향이므로 방향은 아래 -> 오른쪽 -> 위 -> 왼쪽 순서이다. if(nx < cr[1] || nx >= r || ny < 0 || ny >= c) { ddir = (ddir + 3) % 4; }else { if(map[nx][ny] != 0) { if(dr == cr[1] && dc == 0) { map[nx][ny] = 0; }else { map[dr][dc] = map[nx][ny]; map[nx][ny] = 0; } } dr = nx; dc = ny; } } } public static void main(String[] args) throws Exception { 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]; cr = new int[2]; 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(map[i][j] == -1) { cr[idx++] = i; } } } // 시간 t 만큼 미세먼지의 확산과 공기청정기의 동작을 반복한다. while(t > 0) { dust(); cleaner(); t--; } int total = 0; // 남아있는 미세먼지를 계산한다. for(int i = 0; i<r; i++) { for(int j = 0; j<c; j++) { if(map[i][j] == 0 || map[i][j] == -1) continue; total += map[i][j]; } } System.out.println(total); } }
코드를 구현하면서 공기청정기의 순환을 처리하는 부분이 조금 까다롭습니다.
공기 청정기는 각 시계방향과 반시계 방향으로 돌면서 미세먼지를 한 칸씩 밀지만, 이와 같이 할 경우 다음 칸에 미세먼지가 존재하면 이를 처리하는 과정이 복잡하므로 역방향으로 탐색하여 미세먼지를 한 칸씩 끌어당겨 주도록 합시다.
또한 공기청정기가 순환하는 유효범위는 상/하를 구분하여 각각의 행 범위를 잘 조절해주도록 하는 것이 포인트 입니다.
'알고리즘 > PS' 카테고리의 다른 글
[PS] 백준 2933 - 미네랄 (0) | 2020.05.03 |
---|---|
[PS] 백준 2467 - 용액 (0) | 2020.04.26 |
[PS] 백준 2638 - 치즈 (0) | 2020.04.25 |
[SW 역량테스트 기출풀이] 백준 - 17143 낚시왕 (0) | 2020.04.16 |
[SW 역량테스트 기출풀이] 백준 - 15685 드래곤 커브 (0) | 2020.04.11 |