SW Expert Academy - 5656. 벽돌 깨기

5 minute read

문제링크

문제설명

  • W x H 크기의 배열이 주어진다.
  • 배열의 0이 아닌 칸에는 벽돌이 있으며 숫자는 벽돌이 깨졌을 때 옆으로 퍼져나가는 정도를 의미한다.
    • 벽돌은 숫자 1~9로 표현도니다.
    • 벽돌은 구슬을 맞아 깨지면 상하좌우 방향으로 (벽돌에 적힌 숫자 - 1) 만큼 옆에 있는 벽돌들도 함께 제거된다.
    • 제거되는 범위 내에 있는 벽돌은 모두 동시에 제거된다.
  • N, W, H 와 벽돌의 정보가 주어질 때 N개의 구슬을 떨어뜨려 최대한 많은 벽돌을 제거하고 이때 남은 벽돌의 개수를 출력하라

문제접근

  • 재귀 를 활용한 완전탐색 으로 풀었다.
  • 구슬은 [0, W-1] 인덱스에 해당하는 위치에서 N번 떨어뜨릴 수 있는데, 이때 같은 자리에 중복하여 떨어뜨릴 수 있다.
  • 구슬이 떨어지면서 하나의 벽돌을 깨면 연쇄효과로 주위에 있는 벽돌들이 함께 깨질 수 있는데 모든 벽돌이 동시에 깨져야 한다. 따라서, 벽돌이 깨지는 순간마다 벽돌들의 정보를 갱신하지 않고 깨질 수 있는 모든 벽돌이 깨지고 나서 벽돌 정보를 갱신해야 한다.
  • 벽돌들이 깨지고 나면 빈 공간이 있는 경우 밑으로 떨어뜨린다.

구현

#include <iostream>
#include <memory.h>
#include <vector>
#include <algorithm>
#include <queue>

#define MAX_N 4
#define MAX_W 12
#define MAX_H 15
#define MAX 1000
using namespace std;

typedef struct brick_info {
	int xpos;
	int ypos;
	int val;
}BrickInfo;

int N, W, H;
int dx[4] = { -1, 1, 0, 0 };
int dy[4] = { 0, 0, -1, 1 };
int board[MAX_H][MAX_W];

void breakBricks(int cidx) {
	// 깨야하는 벽돌들의 좌표
	queue<BrickInfo> cand;
	BrickInfo cBrick;
	for (int r = 0; r < H; r++) {
		if (board[r][cidx] != 0) {
			cBrick.xpos = r;
			cBrick.ypos = cidx;
			cBrick.val = board[r][cidx];
			cand.push(cBrick);
			break;
		}
	}
	if (cand.empty()) return;
	board[cBrick.xpos][cBrick.ypos] = 0;;
	// 연쇄반응
	int rboard[MAX_H][MAX_W];
	memcpy(rboard, board, sizeof(board));
	while (!cand.empty()) {
		cBrick = cand.front();
		cand.pop();
		// 현재 벽돌의 값
		int num = cBrick.val;
		board[cBrick.xpos][cBrick.ypos] = 0;
		rboard[cBrick.xpos][cBrick.ypos] = 0;
		for (int dir = 0; dir < 4; dir++) {
			int nx = cBrick.xpos;
			int ny = cBrick.ypos;
			BrickInfo nBrick;
			for (int i = 0; i < num-1; i++) {
				nx += dx[dir];
				ny += dy[dir];
				if (nx < 0 || nx >= H || ny < 0 || ny >= W) break;
				if (rboard[nx][ny] == 0) continue;
				nBrick.xpos = nx;
				nBrick.ypos = ny;
				nBrick.val = rboard[nx][ny];
				cand.push(nBrick);
				board[nx][ny] = 0;
			}
		}
	}
}

void setBricksToEmpty() {
	int updateBoard[MAX_H][MAX_W];
	memset(updateBoard, 0, sizeof(updateBoard));

	int cRow = H - 1;
	for (int c = 0; c < W; c++) {
		for (int r = H-1; r >= 0; r--) {
			if (board[r][c] != 0) {
				updateBoard[cRow--][c] = board[r][c];
			}
		}
		cRow = H - 1;
	}
	memcpy(board, updateBoard, sizeof(board));
}

int countBricks() {
	int count = 0;
	for (int r = 0; r < H; r++) {
		for (int c = 0; c < W; c++) {
			if (board[r][c] != 0) count++;
		}
	}
	return count;
}

int shootMarble(int cnt) {
	// 구슬 N번을 모두 쏜 경우
	if (cnt == N) {
		// 남아있는 벽돌의 개수를 반환
		return countBricks();
	}

	int ret = MAX;
	for (int i = 0; i < W; i++) {
		int cidx = i;
		int cboard[MAX_H][MAX_W];
		memcpy(cboard, board, sizeof(board));
		breakBricks(cidx);
		setBricksToEmpty();
		ret = min(ret, shootMarble(cnt + 1));
		memcpy(board, cboard, sizeof(cboard));
	}
	return ret;
}

int main(int argc, char** argv)
{
	cin.tie(NULL);
	ios_base::sync_with_stdio(false);
	int test_case;
	int T;
	// freopen("input.txt", "r", stdin);
	cin >> T;
	for (test_case = 1; test_case <= T; ++test_case)
	{
		cin >> N >> W >> H;
		memset(board, 0, sizeof(board));
		for (int r = 0; r < H; r++) {
			for (int c = 0; c < W; c++) {
				cin >> board[r][c];
			}
		}
		int ans = shootMarble(0);
		cout << "#" << test_case << " " << ans << "\n";
	}
	return 0;//정상종료시 반드시 0을 리턴해야합니다.
}

구현2

#include <iostream>
#include <memory.h>
#include <vector>
#include <algorithm>
#include <climits>

#define MAX_W 12
#define MAX_H 15
#define EMPTY 0

using namespace std;

int N, W, H;
int board[MAX_H][MAX_W];
int visited[MAX_H][MAX_W];
vector<int> cand;
int dx[4] = { -1, 1, 0, 0 };
int dy[4] = { 0, 0, -1, 1 };

void printBoard()
{
	printf("------------------------\n");
	for (int r = 0; r < H; r++) {
		for (int c = 0; c < W; c++) {
			printf("%d ", board[r][c]);
		}
		printf("\n");
	}
}

void init()
{
	memset(board, 0, sizeof(board));
	cand.clear();
}

void input()
{
	init();
	cin >> N >> W >> H;
	for (int r = 0; r < H; r++) {
		for (int c = 0; c < W; c++) {
			cin >> board[r][c];
		}
	}
}

void fallBlocks()
{
	int cpyBoard[MAX_H][MAX_W];
	memset(cpyBoard, 0, sizeof(cpyBoard));

	for (int c = 0; c < W; c++) {
		int nr = H - 1;
		for (int r = H - 1; r >= 0; r--) {
			if (board[r][c] == EMPTY) continue;
			cpyBoard[nr--][c] = board[r][c];
		}
	}
	memcpy(board, cpyBoard, sizeof(board));
}

void explosionChain(int sx, int sy)
{
	int length = board[sx][sy] - 1;

	for (int dir = 0; dir < 4; dir++) {
		int nx = sx;
		int ny = sy;
		for (int i = 0; i < length; i++) {
			nx += dx[dir];
			ny += dy[dir];
			if (nx < 0 || nx >= H || ny < 0 || ny >= W) break;
			if (board[nx][ny] == EMPTY) continue;
			if (visited[nx][ny]) continue;
			visited[nx][ny] = true;
			explosionChain(nx, ny);
			board[nx][ny] = 0;
		}
	}
}

void explosion(int col)
{
	int sx = -1, sy = -1;
	for (int r = 0; r < H; r++) {
		if (board[r][col] > 0) {
			sx = r;
			sy = col;
			break;
		}
	}

	if (sx == -1 && sy == -1) return;
	memset(visited, false, sizeof(visited));
	visited[sx][sy] = true;
	explosionChain(sx, sy);
	board[sx][sy] = 0;
	fallBlocks();
	// printBoard();
}

int getRemainBlocks()
{
	int ret = 0;
	for (int r = 0; r < H; r++) {
		for (int c = 0; c < W; c++) {
			if (board[r][c] == EMPTY) continue;
			ret++;
		}
	}
	return ret;
}

int explosionSelected()
{
	for (auto col : cand) {
		explosion(col);
	}

	return getRemainBlocks();
}

int playGame(int cnt)
{
	if (cnt == N) return explosionSelected();

	int ret = INT_MAX;
	for (int i = 0; i < W; i++) {
		int cboard[MAX_H][MAX_W];
		memcpy(cboard, board, sizeof(board));
		cand.push_back(i);
		ret = min(ret, playGame(cnt + 1));
		cand.pop_back();
		memcpy(board, cboard, sizeof(board));
	}
	return ret;
}

void test()
{
	input();
	cand.push_back(2);
	cand.push_back(2);
	cand.push_back(6);
	explosion(2);
	explosion(2);
	// explosion(6);
	// cout << explosionSelected() << endl;
}

int main(int argc, char** argv)
{
	int test_case;
	int T = 0;
	
	// freopen("input.txt", "r", stdin);
	cin >> T;
	// test();
	for (test_case = 1; test_case <= T; ++test_case)
	{
		input();
		cout << "#" << test_case << " " << playGame(0) << endl;
	}
	return 0;//정상종료시 반드시 0을 리턴해야합니다.
}

Leave a comment