[BOJ 20058] 마법사 상어와 파이어스톰
업데이트:
문제
- BOJ 20058
- 문제의 저작권은 Baekjoon Online Judge에 있습니다.
접근방식
1. 시계 방향 90도 회전
2^N*2^N으로 격자를 나눈 후 90도 회전한 결과를 tmp
배열에 넣는다.
2. 얼음 줄이기
배열 전체를 돌면서 상화좌우 탐색하여 인접한 칸에 얼음이 있으면 cnt
를 증가시킨다.
모든 탐색 후 cnt가 3개 미만이면 check
배열에 true표시한다. 이때 주의할 점은 탐색하면서 얼음을 바로 줄이면 안된다.
다음에 검사할 배열값에도 영향을 주기 때문에 모든 탐색이 끝난 후에 다시 for문을 돌면서 check
에 true
표시해준 값의 얼음의 양을 하나 줄여야 한다.
3. 합 구하기
모든 시전이 끝나면 남아있는 얼음을 구하기 위해 배열값을 누적합한다.
4. 가장 큰 덩어리 구하기
가장 큰 덩어리를 만들기 위해서는 연결된 그룹을 탐색해야 하므로 bfs탐색을 해서 가장 큰 덩어리의 칸 개수를 구한다.
2번에서 얼음을 바로 줄여서 ‘틀렸습니다’가 나왔다. 틀리고 나서 깨달았다..이런 자잘한 실수를 줄여야할텐데ㅠㅠ
import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.OutputStreamWriter;
import java.util.LinkedList;
import java.util.Queue;
import java.util.StringTokenizer;
public class Main {
static int N, Q;
static int[][] map, tmp;
static int[][] dInfo = { { -1, 0 }, { 1, 0 }, { 0, -1 }, { 0, 1 } };
static Queue<Point> queue;
static boolean[][] visited;
static boolean[][] checked;
static int size;
static int ans_sum;
static int ans_max;
static class Point {
int x;
int y;
public Point(int x, int y) {
this.x = x;
this.y = y;
}
}
public static void main(String[] args) throws IOException {
BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
BufferedWriter bw = new BufferedWriter(new OutputStreamWriter(System.out));
StringTokenizer st = new StringTokenizer(br.readLine());
N = Integer.parseInt(st.nextToken());
Q = Integer.parseInt(st.nextToken());
size = (int) Math.pow(2, N); // 한변의 길이
map = new int[size][size];
for (int i = 0; i < size; i++) {
st = new StringTokenizer(br.readLine());
for (int j = 0; j < size; j++) {
map[i][j] = Integer.parseInt(st.nextToken());
}
} // End input
st = new StringTokenizer(br.readLine());
for (int q = 0; q < Q; q++) {
int l = Integer.parseInt(st.nextToken()); // 분할 크기
tmp = new int[size][size];
// 1. 90도 회전
for (int i = 0; i < size; i += (int) Math.pow(2, l)) {
for (int j = 0; j < size; j += (int) Math.pow(2, l)) {
rotate(i, j, (int) Math.pow(2, l));
}
}
checked = new boolean[size][size];
// 2. 얼음 줄이기
for (int i = 0; i < size; i++) {
for (int j = 0; j < size; j++) {
int cnt = 0;
if (map[i][j] == 0)
continue;
for (int d = 0; d < 4; d++) {
int ni = i + dInfo[d][0];
int nj = j + dInfo[d][1];
if (ni >= 0 && ni < size && nj >= 0 && nj < size && map[ni][nj] > 0)
cnt++;
}
if (cnt < 3)
checked[i][j] = true;
}
}
for (int i = 0; i < size; i++) {
for (int j = 0; j < size; j++) {
if (checked[i][j])
map[i][j]--;
}
}
} // End 시전
// 3. 합 구하기
for (int i = 0; i < size; i++) {
for (int j = 0; j < size; j++) {
if (map[i][j] <= 0)
continue;
ans_sum += map[i][j];
}
}
// 4. 가장 큰 덩어리 구하기
queue = new LinkedList<>();
visited = new boolean[size][size];
for (int i = 0; i < size; i++) {
for (int j = 0; j < size; j++) {
if (!visited[i][j] && map[i][j] > 0) {
queue.add(new Point(i, j));
visited[i][j] = true;
bfs();
}
}
}
bw.write(ans_sum + "\n");
bw.write(ans_max + "");
bw.flush();
bw.close();
br.close();
}
private static void bfs() {
int cnt = 1;
while (!queue.isEmpty()) {
Point p = queue.poll();
for (int d = 0; d < 4; d++) {
int ni = p.x + dInfo[d][0];
int nj = p.y + dInfo[d][1];
if (ni >= 0 && ni < size && nj >= 0 && nj < size && !visited[ni][nj] && map[ni][nj] > 0) {
queue.add(new Point(ni, nj));
visited[ni][nj] = true;
cnt++;
}
}
}
ans_max = Math.max(ans_max, cnt);
}
private static void rotate(int x, int y, int length) {
for (int i = 0; i < length; i++) { // 90도 회전
for (int j = 0; j < length; j++) {
tmp[j][length - i - 1] = map[x + i][y + j];
}
}
for (int i = 0; i < length; i++) { // 원래 배열에 복사
for (int j = 0; j < length; j++) {
map[x + i][y + j] = tmp[i][j];
}
}
}
}