https://www.acmicpc.net/problem/2234
2234번: 성곽
첫째 줄에 두 정수 N, M이 주어진다. 다음 M개의 줄에는 N개의 정수로 벽에 대한 정보가 주어진다. 벽에 대한 정보는 한 정수로 주어지는데, 서쪽에 벽이 있을 때는 1을, 북쪽에 벽이 있을 때는 2를,
www.acmicpc.net
코드설명
비트마스킹과 BFS 문제입니다.
문제에서 벽이 주어지는
서쪽 : 1 -> 1 (2진수)
북쪽 : 2 -> 10 (2진수)
동쪽 : 4 -> 100 (2진수)
남쪽 : 8 -> 1000 (2진수)
위와 같이 주어집니다.
이떄 만약 11이 주어진다고 가정해보겠습니다. 11은 1 + 2 + 8 로, 즉, 서쪽벽, 북쪽벽, 남쪽벽으로 이루어져있다고 할 수 있습니다. 문제의 예시로 보면, map[0][0] 을 의미합니다. 해당 문제에 존재하는 그림을 참고하시기 바랍니다.
7 4 11 6 11 6 3 10 6 7 9 6 13 5 15 5 1 10 12 7 13 7 5 13 11 10 8 10 12 13
비트마스킹을 활용하여 서쪽, 북쪽, 동쪽, 남쪽 4가지 방향이 포함되었는지 확인하기 위해 & 연산자를 활용하여 값이 0 보다 크다면 해당 벽으로 이루어진 숫자이므로 벽이 존재하는것으로 판단합니다.
if( (map[temp.r][temp.c] & (1 << dir) ) == 0) { //만약 해당방향에 벽이 없다면, 이동시킨다. 만약 벽이 존재한다면 1 이상의 값이 나옵니다. Indexing[nr][nc][0] = idx; q.offer(new Node(nr, nc)); }
비트마스킹을 통해 벽을 판단하는 방법을 정했습니다.
이제는 해당 벽에 맞추어서 일반적인 BFS를 진행하면 됩니다.
이때, 0: 현재 방의 Index를 저장합니다. 1: 현재 방의 최대 카운트값을 저장합니다.
Indexing[][][2] 2차원 배열로 선언하여 위의 Indexing Group을 정하고, 방의 최대 카운트값 또한 [3.하나의 벽을 제거하여 얻을 수 있는 가장 넓은 방의 크기] 을 구하기 위해 저장합니다.
Indexing = new int[M][N][2]; //0: 현재 방의 Index를 저장합니다. 1: 현재 방의 최대 카운트값을 저장합니다.
1. 이 성에 있는 방의 개수는 Indexing의 최대값으로 구할 수 있습니다.
2. 가장 넓은 방의 넓이는 BFS를 진행하며 가장 큰 BFS의 cnt값으로 갱신해갑니다.
3. 하나의 벽을 제거하여 얻을 수 있는 가장 넓은 방의 크기는 Indexing[][]2] 에서 모든 배열을 돌면서 만약 상하좌우의 Indexing Group이 다르다면, 벽에 막혀 있는것이므로 벽을 부수고 합칠 수 있습니다. 이떄 최대값으로 갱신합니다.
제가 실수했었던 부분은, 벽의 위치를 계산할때 현재 map의 위치에서 벽이 존재하는지 확인했어야했는데 이동한 방향의 벽을 계산하여서 오류가 있었습니다.
if( (map[temp.r][temp.c] & (1 << dir) ) == 0) { //만약 해당방향에 벽이 없다면, 이동시킨다. 만약 벽이 존재한다면 1 이상의 값이 나옵니다. Indexing[nr][nc][0] = idx; q.offer(new Node(nr, nc)); }
코드
import java.io.BufferedReader; import java.io.IOException; import java.io.InputStreamReader; import java.util.LinkedList; import java.util.List; import java.util.Queue; import java.util.StringTokenizer; public class Main { public static int N, M; public static int[][] map; public static int answer = Integer.MAX_VALUE; public static int[][][] Indexing; public static int idx = 1; public static int[] dx= {0, -1, 0, 1}; public static int[] dy = {-1, 0, 1, 0}; public static int answerRoomCnt = 0, answerMaxRoom = 0, answerBreakWallRoom = 0; public static StringBuilder sb = new StringBuilder(); public static void main(String[] args) throws IOException{ BufferedReader br = new BufferedReader(new InputStreamReader(System.in)); StringTokenizer st = new StringTokenizer(br.readLine()); N = Integer.parseInt(st.nextToken()); M = Integer.parseInt(st.nextToken()); map = new int[M][N]; Indexing = new int[M][N][2]; //0: 현재 방의 Index를 저장합니다. 1: 현재 방의 최대 카운트값을 저장합니다. for(int i=0;i<M;i++) { st = new StringTokenizer(br.readLine()); for(int j=0;j<N;j++) { map[i][j] = Integer.parseInt(st.nextToken()); } } for(int i=0;i<M;i++) { for(int j=0;j<N;j++) { if(Indexing[i][j][0] == 0) { simulate(new Node(i, j)); } } } // for(int i=0;i<M;i++) { // for(int j=0;j<N;j++) { // System.out.print(" "+Indexing[i][j][0]); // } // System.out.println(); // } for(int i=0;i<M;i++) { for(int j=0;j<N;j++) { getMaxWallBreakCount(new Node(i, j)); } } answerRoomCnt = idx - 1; //1. 이 성에 있는 방의 개수 answerMaxRoom = answerMaxRoom; //2. 가장 넓은 방의 넓이. (simulate함수에서 구했었습니다.) answerBreakWallRoom = answerBreakWallRoom; //3. 하나의 벽을 제거하여 얻을 수 있는 가장 넓은 방의 크기 System.out.println(answerRoomCnt); System.out.println(answerMaxRoom); System.out.println(answerBreakWallRoom); } public static void simulate(Node start) { Queue<Node> q = new LinkedList<>(); List<Node> storeVisitedRoom = new LinkedList<Node>(); int roomCnt = 0; Indexing[start.r][start.c][0] = idx; q.offer(start); while(!q.isEmpty()) { Node temp = q.poll(); roomCnt += 1; storeVisitedRoom.add(temp); for(int dir = 0; dir < 4; dir ++) { int nr = temp.r + dx[dir]; int nc = temp.c + dy[dir]; if(nr < 0 || nr >= M || nc < 0 || nc >= N) continue; if(Indexing[nr][nc][0] != 0 ) continue; if( (map[temp.r][temp.c] & (1 << dir) ) == 0) { //만약 해당방향에 벽이 없다면, 이동시킨다. 만약 벽이 존재한다면 1 이상의 값이 나옵니다. Indexing[nr][nc][0] = idx; q.offer(new Node(nr, nc)); } } } for(int i=0;i<storeVisitedRoom.size();i++) { //해당 그룹의 벽 개수를 전체 그룹에 모두 동기화시킵니다. Node temp = storeVisitedRoom.get(i); Indexing[temp.r][temp.c][1] = roomCnt; } answerMaxRoom = Math.max(answerMaxRoom, roomCnt); idx += 1;//방의 인덱싱 1개 증가시킵니다. } public static void getMaxWallBreakCount(Node start) { Node temp = start; for(int dir = 0; dir<4; dir++) { int nr = temp.r + dx[dir]; int nc = temp.c + dy[dir]; if(nr < 0 || nr >= M || nc < 0 || nc >= N) continue; if(Indexing[nr][nc][0] != Indexing[temp.r][temp.c][0]) { //만약 인덱싱이 옆과 다르다면 해당 벽을 부순뒤 합칩니다. answerBreakWallRoom = Math.max(answerBreakWallRoom, Indexing[nr][nc][1] + Indexing[temp.r][temp.c][1]); } } } } class Node{ int r; int c; public Node(int r, int c) { this.r = r; this.c = c; } }
'알고리즘 > 백준' 카테고리의 다른 글
[백준] 16235 나무 재테크 - 시뮬레이션 + 우선순위큐(Priority Queue) + 자료구조 + 시간초과 JAVA (0) | 2023.11.13 |
---|---|
[백준] 1525 퍼즐 - BFS(너비우선탐색) + HashMap + StringBuilder JAVA (0) | 2023.11.11 |
[백준] 2146 다리 만들기 - BFS(너비우선탐색) JAVA (0) | 2023.11.11 |
[백준] 1726 로봇 - 너비우선탐색(BFS) JAVA (0) | 2023.11.10 |
[백준] 17142 연구소 3 - 조합(Combination) + 너비우선탐색(BFS) + DFS(깊이우선탐색) JAVA (0) | 2023.11.09 |