console.log

[SWEA] 5656 벽돌 깨기 JAVA 본문

알고리즘/SW Expert Academy

[SWEA] 5656 벽돌 깨기 JAVA

foresight 2023. 10. 11. 13:52

https://swexpertacademy.com/main/code/problem/problemDetail.do?contestProbId=AWXRQm6qfL0DFAUo 

 

SW Expert Academy

SW 프로그래밍 역량 강화에 도움이 되는 다양한 학습 컨텐츠를 확인하세요!

swexpertacademy.com

 

메모리 : 111,352kb
실행시간 : 501ms

 

문제분석

완탐 + bfs
맵이 계속 바뀌기 때문에 구슬 객체를 만들어서 맵을 관리해야 한다.
시간을 줄이기 위해 중복순열 대신 bfs를 이용해 완탐한다.

 

걸린시간

2시간 ⏱️

 

풀이방법

1. 구슬 객체 생성 (구슬을 쳤을 때 맞는 벽돌 위치, 구슬로 인해 바뀌는 맵 저장하기 위한 객체)
2. 맵 입력받기
3. 처음 q에 맨 윗줄 넣어두기 (0이 아닌 곳만)
4. 구슬치기 (m번)
	4-1. 맵 변경해주는 함수 부르기 (영향받는 모든 벽돌 제거, 빈 공간 없애기)
	4-2. 변경된 맵 받아오기
	4-3. 맨 윗줄 넣기 (0이 아닌 곳만)
		----> 이때, 아무곳도 칠 수 없으면 벽돌이 없다는 뜻 ... 반복문 빠져나오기
5. 구슬을 m번 치거나 벽돌이 없어서 반복문을 빠져나오면
   q에 들어있는 구슬 객체의 map을 꺼내서 벽돌 개수 세기 ---> 세면서 최소값 갱신
6. 최소값 출력

 

포인트

문제가 생각보다 복잡해서 각각의 함수를 잘 작성해야 한다.

최적화 할 필요가 있는 풀이인 것 같다.

 

코드

package study.day0908;

import java.io.BufferedReader;
import java.io.FileInputStream;
import java.io.InputStreamReader;
import java.util.ArrayDeque;
import java.util.Arrays;
import java.util.Queue;
import java.util.StringTokenizer;

public class SWEA_5656_벽돌깨기 {	// 메모리 : 111352kb	실행시간 : 501ms
	static int H, W;

	// 구슬 객체
	public static class Marble {
		int r;
		int c;
		int[][] map = new int[H][W];

		public Marble(int r, int c, int[][] map) {
			this.r = r;
			this.c = c;
			for (int i = 0; i < H; i++) {
				this.map[i] = Arrays.copyOf(map[i], W);
			}
		}
	}
	public static void main(String[] args) throws Exception {
		System.setIn(new FileInputStream("data/5656.txt"));
		BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
		StringBuilder sb = new StringBuilder();
		StringTokenizer st;

		int T = Integer.parseInt(br.readLine());

		for (int t = 1; t <= T; t++) {
			// 결과값 초기화
			int minCnt = Integer.MAX_VALUE;

			st = new StringTokenizer(br.readLine());
			int N = Integer.parseInt(st.nextToken());
			W = Integer.parseInt(st.nextToken());
			H = Integer.parseInt(st.nextToken());

			int[][] map = new int[H][W];

			// 벽돌 맵 입력받기
			for (int i = 0; i < H; i++) {
				st = new StringTokenizer(br.readLine());
				for (int j = 0; j < W; j++) {
					map[i][j] = Integer.parseInt(st.nextToken());
				}
			}

			Queue<Marble> q = new ArrayDeque<>();

			// 처음에 맨 윗줄 모두 넣기
			for (int i = 0; i < W; i++) {
				for (int j = 0; j < H; j++) {
					if(map[j][i] != 0) {
						q.offer(new Marble(j, i, map));
						break;
					}
				}
			}

			// 구슬치기
			outer : for (int m = 0; m < N; m++) {

				int size = q.size();
				for (int s = 0; s < size; s++) {
					Marble marble = q.poll();

					// 해당 구슬에 대해 변경된 벽돌 맵 받아오기
					int[][] newMap = new int[H][W];
					int[][] map2 = remove(marble.r, marble.c, marble.map).clone();
					for (int i = 0; i < H; i++) {
						newMap[i] = Arrays.copyOf(map2[i], W);
					}

					// 벽돌이 없을 경우 반복문을 빠져나오기 위해 해당 부분 판별해줄 변수
					boolean clear = false;
					// 맨 윗줄 전부 넣기
					for (int i = 0; i < W; i++) {
						for (int j = 0; j < H; j++) {
							if(newMap[j][i] != 0) {
								q.offer(new Marble(j, i, newMap));
								clear = true;
								break;
							}
						}
					}
					if(!clear) {
						q.clear();
						break outer;
					}
				}
			}

			// 구슬 다 친 뒤에 최소값 찾기
			if(q.isEmpty()) minCnt = 0;
			else {
				int size = q.size();
				for (int s = 0; s < size; s++) {
					int cnt = 0;
					Marble marble = q.poll();
					int[][] map2 = marble.map.clone();

					for (int i = 0; i < H; i++) {
						for (int j = 0; j < W; j++) {
							if(map2[i][j] > 0) cnt++;
						}
					}
					minCnt = Math.min(minCnt, cnt);
				}
			}
			sb.append("#" + t + " " + minCnt + "\n");
		}
		System.out.println(sb);
	}

	// 구슬쳤을 때 바뀌는 맵 계산하는 함수
	private static int[][] remove(int r, int c, int[][] map) {
		int[][] newMap = new int[H][W];

		int range = map[r][c];
		Queue<int[]> q = new ArrayDeque<>();
		q.offer(new int[] {r, c, range});

		// 벽돌제거
		while(!q.isEmpty()) {
			int size = q.size();
			for (int s = 0; s < size; s++) {
				int[] temp = q.poll();
				int nr = temp[0];
				int nc = temp[1];
				int len = temp[2];
				map[nr][nc] = 0;
				for (int d = 1; d < len; d++) {
					if(nr - d >= 0 && nr - d < H && map[nr - d][nc] != 0) {
						if(map[nr - d][nc] > 1) q.offer(new int[] {nr - d, nc, map[nr - d][nc]});
						map[nr - d][nc] = 0;
					}
					if(nr + d >= 0 && nr + d < H && map[nr + d][nc] != 0) {
						if(map[nr + d][nc] > 1) q.offer(new int[] {nr + d, nc, map[nr + d][nc]});
						map[nr + d][nc] = 0;
					}
					if(nc - d >= 0 && nc - d < W && map[nr][nc - d] != 0) {
						if(map[nr][nc - d] > 1) q.offer(new int[] {nr, nc - d, map[nr][nc - d]});
						map[nr][nc - d] = 0;
					}
					if(nc + d >= 0 && nc + d < W && map[nr][nc + d] != 0) {
						if(map[nr][nc + d] > 1) q.offer(new int[] {nr, nc + d, map[nr][nc + d]});
						map[nr][nc + d] = 0;
					}
				}
			}
		}

		// 빈 공간 없애기
		for (int i = 0; i < W; i++) {
			int[] num = new int[H];
			int idx = 0;
			for (int j = 0; j < H; j++) {
				if(map[j][i] > 0) {
					num[idx++] = map[j][i];
				}
			}
			for (int k = idx-1, n = H - 1; k >= 0; k--) {
				newMap[n--][i] = num[k];
			}
		}
		return newMap;
	}
}

'알고리즘 > SW Expert Academy' 카테고리의 다른 글

[SWEA] 2105 디저트 카페 JAVA  (1) 2023.10.16
[SWEA] 4008 숫자 만들기 JAVA  (0) 2023.10.15
[SWEA] 5658 보물상자 비밀번호 JAVA  (0) 2023.10.10
[SWEA] 2115 벌꿀채취 JAVA  (1) 2023.10.09
[SWEA] 1952 수영장 JAVA  (0) 2023.10.08