[C++]백준20058번 마법사 상어와 파이어스톰

3 minute read

문제는 다음과 같다.

오늘은 백준20058번 마법사 상어와 파이어스톰 문제를 풀어보도록 하자. 작년 하반기 S전자 기출문제로 역시 시뮬레이션 문제이다. 이번 문제를 풀면서 얻은 것이 있다. 바로 pow()함수 를 쓰지말자.

pow()함수 를 쓰는건 나쁘지 않다. 하지만 무분별한 pow함수 때문에 TLE 에 걸려버렸다. 이유를 알 수 없었다. pow함수가 의심쩍어 시간복잡도를 확인해보니 O(N)인걸 확인하고 기겁을 했다. 이렇게 큰 함수를 아무 생각없이 쓰고 있었다는 것에 비통함을 금치못했다.

그래서 Bitmasking을 쓰기로 했다. 문제를 보면 2의 n승수만큼 자르기 때문에 N = 1«N; 이런 식으로 Bitmasking을 해서 고정 값을 만들고 for문에 적용했다.

TLE 가 난 코드를 보면.. for(int i=0;i<pow(2,N);i++)에 또 for문을 곁들인.. 매우 비참한 코드였다. 만일 N이 8이라면 64번을 또 곱하는 불상사가 발생한다.

따라서 pow(2,N)을 고정 값으로 설정하던지, Bitmasking한 값을 고정 값으로 설정하고 풀어야 했다. 문제는 구현, BFS라 그리 복잡하지 않았다. 내가 문제를 풀면서 얻어가는게 있다는 것에 만족한다.

                                     [코드]
/*
1. 격자L를 나눈다.
2. 2^L x 2^L만큼의 배열을 시계방향으로 90도 회전
3. 얼음이 있는 칸 3개가 없으면 얼음의 양이 1 줄어든다.

answer : 남아있는 얼음의 합 ,
		 남아있는 가장 큰 덩어리가 차지하는 칸의 갯수
		 
*/
#include <iostream>
#include <queue>
#include <vector>
using namespace std;

struct pos {
	int y;
	int x;
};

int map[64][64];//맵
int temp_location[64][64];//격자 나누고 맵에 넣어줄 것
bool visit[64][64];
int dir[4][2] = { {-1,0},{0,1},{1,0},{0,-1} };
int N, Q, level, first_answer = 0, second_answer = 0;//맵 크기=2^N, Q=격자 쪼갤 횟수,level=쪼개는 크기

bool Inbound(int y, int x) {
	return y >= 0 && x >= 0 && y < N && x < N;
}

void mass_cnt(int a, int b) {
	queue<pos>mass_Q;
	int cnt = 1;
	visit[a][b] = true;
	mass_Q.push({ a,b });

	while (!mass_Q.empty()) {
		int y = mass_Q.front().y;
		int x = mass_Q.front().x;

		mass_Q.pop();

		for (int i = 0; i < 4; i++) {
			int my = y + dir[i][0];
			int mx = x + dir[i][1];

			if (Inbound(my,mx)) {
				if (map[my][mx] > 0 && !visit[my][mx]) {
					visit[my][mx] = true;
					cnt++;
					mass_Q.push({ my,mx });
				}
			}
		}
	}
	if (second_answer < cnt)second_answer = cnt;
}

void melting(int N) {
	queue<pos>Q;
	vector<pos>V;
	for (int i = 0; i < N; i++) {
		for (int j = 0; j < N; j++) {
			if (map[i][j] > 0) {//얼음있으면 큐에 넣기
				Q.push({ i,j });
			}
		}
	}

	while (!Q.empty()) {
		int y = Q.front().y;
		int x = Q.front().x;
		int cnt = 4;
		Q.pop();

		for (int i = 0; i < 4; i++) {
			int my = y + dir[i][0];
			int mx = x + dir[i][1];

			if (!Inbound(my,mx)) {
				cnt--;
				continue;
			}
			if (map[my][mx] <= 0) {
				cnt--;
			}
		}
		if (cnt < 3) {
			V.push_back({ y,x });
		}
	}

	for (int i = 0; i < V.size(); i++) {
		map[V[i].y][V[i].x]--;
	}
}

void clock_lotate(int y, int x, int M) {//기준점과 쪼개는 크기
	for (int i = 0; i < M; i++) {
		for (int j = 0; j < M; j++) {
			temp_location[j][M - 1 - i] = map[y+i][x+j];
		}
	}

	for (int i = 0; i < M; i++) {
		for (int j = 0; j < M; j++) {
			map[y+i][x+j] = temp_location[i][j];
		}
	}
}

int main() {
	ios::sync_with_stdio(false);
	cin.tie(0);

	cin >> N >> Q;
	N = 1 << N;
	for (int i = 0; i < N; i++) {
		for (int j = 0; j < N; j++) {
			cin >> map[i][j];
		}
	}
	for (int i = 0; i < Q; i++) {
		cin >> level;
		int M = 1 << level;
		if (level > 0) {
			for (int i = 0; i < N; i += M) { //회전
				for (int j = 0; j < N; j += M) {
					clock_lotate(i, j, M);
				}
			}
		}
		melting(N);
	}

	for (int i = 0; i < N; i++) {
		for (int j = 0; j < N; j++) {
			if (map[i][j] > 0) {
				first_answer += map[i][j];//1번정답
			}
		}
	}
	
	for (int i = 0; i < N; i++) {
		for (int j = 0; j < N; j++) {
			if (map[i][j] != 0 && !visit[i][j]) {
				mass_cnt(i, j);//2번정답
			}
		}
	}

	cout << first_answer<<'\n';
	cout << second_answer;
}