Algorithm/Problem
미세먼지 안녕!(BJ_G4_17144)
당진개발자
2024. 2. 27. 23:07
1. 문제 링크
https://www.acmicpc.net/problem/17144
17144번: 미세먼지 안녕!
미세먼지를 제거하기 위해 구사과는 공기청정기를 설치하려고 한다. 공기청정기의 성능을 테스트하기 위해 구사과는 집을 크기가 R×C인 격자판으로 나타냈고, 1×1 크기의 칸으로 나눴다. 구사
www.acmicpc.net
2. 나의 코드
메모리: 118100KB
시간: 384ms
코드 길이: 4732B
시간 복잡도 : O(N^2)
설명
- 리스트에 미세먼지 정보들을 저장
- 사방탐색 후 확산
- 바람의 방향을 미리 저장하여 공기청정기 가동
- 마지막으로 다시 맵을 탐색하여 리스트에 미세먼지 정보를 넣는다.
import java.awt.*;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.StringTokenizer;
public class BJ_17144 {
static class Dust {
int r, c, n;
public Dust(int r, int c, int n) {
this.r = r;
this.c = c;
this.n = n;
}
}
// 상 하 좌 우
static int[] dr = {-1, 1, 0, 0};
static int[] dc = {0, 0, -1, 1};
static int[] topD = {0, 3, 1, 2}; // 위에 있는 공기청정기 방향
static int[] bottomD = {1, 3, 0, 2}; // 아래 있는 공기청정기 방향
static Point topM; // 위쪽 공기 청정기 좌표
static Point bottomM; // 아래쪽 공기 청정기 좌표
static int R, C, T, result;
static int[][] map;
static List<Dust> dustList;
public static void main(String[] args) 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];
dustList = new ArrayList<>();
boolean flag = false;
for (int i = 0; i < R; i++) {
st = new StringTokenizer(br.readLine());
for (int j = 0; j < C; j++) {
int num = Integer.parseInt(st.nextToken());
if (num == -1 && !flag) {
map[i][j] = -1;
topM = new Point(i, j);
flag = true;
continue;
} else if (num == -1 && flag) {
map[i][j] = -1;
bottomM = new Point(i, j);
continue;
}
if (num != 0) {
dustList.add(new Dust(i, j, num));
} else {
map[i][j] = num;
}
}
}
while (T-- > 0) {
diffusion(); // 확산
wind(); // 공기 청정기 바람
findDust(); // 미세먼지 찾기
}
System.out.println(result);
}
private static boolean isValid(int r, int c) {
return 0 <= r && r < R && 0 <= c && c < C;
}
private static void wind() {
// 위쪽 바람 처리
int nowX = topM.x - 1;
int nowY = topM.y;
int nowD = 0;
while (true) {
int nr = nowX + dr[topD[nowD]];
int nc = nowY + dc[topD[nowD]];
if (!isValid(nr, nc) || nr > topM.x) {
nowD = (nowD + 1) % 4;
continue;
}
if (nr == topM.x && nc == topM.y) break;
map[nowX][nowY] = map[nr][nc];
nowX = nr;
nowY = nc;
}
map[topM.x][topM.y + 1] = 0;
// 아래쪽 바람 처리
nowX = bottomM.x + 1;
nowY = bottomM.y;
nowD = 0;
while (true) {
int nr = nowX + dr[bottomD[nowD]];
int nc = nowY + dc[bottomD[nowD]];
if (!isValid(nr, nc) || nr < bottomM.x) {
nowD = (nowD + 1) % 4;
continue;
}
if (nr == bottomM.x && nc == bottomM.y) break;
map[nowX][nowY] = map[nr][nc];
nowX = nr;
nowY = nc;
}
map[bottomM.x][bottomM.y + 1] = 0;
}
private static void diffusion() {
map = new int[R][C];
map[topM.x][topM.y] = -1;
map[bottomM.x][bottomM.y] = -1;
for (Dust now : dustList) {
int diffusionCnt = 0; // 확산한 수
for (int d = 0; d < 4; d++) {
int nr = now.r + dr[d];
int nc = now.c + dc[d];
if (isValid(nr, nc) && map[nr][nc] != -1 && now.n / 5 >= 1) {
map[nr][nc] += now.n / 5;
++diffusionCnt;
}
}
map[now.r][now.c] += now.n - now.n / 5 * diffusionCnt;
}
}
private static void findDust() {
dustList = new ArrayList<>();
int sumDust = 0;
for (int i = 0; i < R; i++) {
for (int j = 0; j < C; j++) {
if (map[i][j] != 0 && map[i][j] != -1) {
dustList.add(new Dust(i, j, map[i][j]));
sumDust += map[i][j];
}
}
}
result = sumDust;
}
}
3. 정답 코드
import java.io.BufferedReader;
import java.io.InputStreamReader;
import java.util.StringTokenizer;
public class Main {
static int R, C, T;
static int[] aircon;
static int[] dr = {-1, 0, 1, 0};
static int[] dc = {0, 1, 0, -1};
static int[][] map;
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];
aircon = new int[2];
int airconIdx = 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) {
aircon[airconIdx++] = i;
}
}
}
play();
int answer = calculate();
System.out.println(answer);
}
private static void play() {
for (int time = 0; time < T; time++) {
diffusion();
operate();
}
}
// 미세먼지가 확산된다
private static void diffusion() {
int[][] newMap = new int[R][C];
for (int r = 0; r < R; r++) {
for (int c = 0; c < C; c++) {
// 미세먼지가 있는 칸인 경우
if (map[r][c] > 0) {
int cnt = 0;
int dust = map[r][c] / 5;
for (int dir = 0; dir < 4; dir++) {
int nr = r + dr[dir];
int nc = c + dc[dir];
// 인접한 방향에 칸이 있고, 공기청정기가 없는 경우
if (isIn(nr, nc) && map[nr][nc] > -1) {
newMap[nr][nc] += dust;
cnt++;
}
}
newMap[r][c] += map[r][c] - dust * cnt;
}
}
}
newMap[aircon[0]][0] = -1;
newMap[aircon[1]][0] = -1;
map = newMap;
}
// 공기청정기가 작동한다
private static void operate() {
for (int idx = 0; idx < 2; idx++) {
int r = aircon[idx];
// 공기청정기 위쪽 작동
if (idx == 0) {
int dir = 0;
int nr = r + dr[dir];
int nc = dc[dir];
while (true) {
// 공기청정기로 돌아올 경우
if (nr == r && nc == 0) {
break;
}
// 맵 밖으로 방향을 벗어나는 경우 방향 전환
if (!isIn(nr, nc) || nr > r) {
nr -= dr[dir];
nc -= dc[dir];
dir += 1;
nr += dr[dir];
nc += dc[dir];
continue;
}
// 먼지의 이동 방향이 공기청정기가 아닌 경우
if (!(nr - dr[dir] == r && nc - dc[dir] == 0)) {
map[nr - dr[dir]][nc - dc[dir]] = map[nr][nc];
}
map[nr][nc] = 0;
// 먼지 탐색 이동
nr += dr[dir];
nc += dc[dir];
}
// 공기청정기 아래쪽 작동
} else {
int dir = 2;
int nr = r + dr[dir];
int nc = dc[dir];
while (true) {
// 공기청정기로 돌아올 경우
if (nr == r && nc == 0) {
break;
}
// 맵 밖으로 방향을 벗어나는 경우 방향 전환
if (!isIn(nr, nc) || nr < r) {
nr -= dr[dir];
nc -= dc[dir];
dir = (dir + 3) % 4;
nr += dr[dir];
nc += dc[dir];
continue;
}
// 먼지의 이동 방향이 공기청정기가 아닌 경우
if (!(nr - dr[dir] == r && nc - dc[dir] == 0)) {
map[nr - dr[dir]][nc - dc[dir]] = map[nr][nc];
}
map[nr][nc] = 0;
// 먼지 탐색 이동
nr += dr[dir];
nc += dc[dir];
}
}
}
}
// 미세먼지 총량 계산
private static int calculate() {
int sum = 0;
for (int i = 0; i < R; i++) {
for (int j = 0; j < C; j++) {
if (map[i][j] > 0) {
sum += map[i][j];
}
}
}
return sum;
}
private static boolean isIn(int r, int c) {
return r >= 0 && c >= 0 && r < R && c < C;
}
}
4. 배운 점
- 쓸데 없는 리스트는 만들지 않는게 더 좋을 것 같다. (메모리를 많이 잡아 먹는다.)
- 맵 복사를 사용하는 방법도 괜찮은 것 같다.