CS/Algorithm
[SWEA][C++] 5656 벽돌 깨기
별토끼.
2020. 4. 17. 16:29
반응형
문제
SW Expert Academy 5656번 벽돌 깨기
풀이
1. 구슬을 쏠 index(w,가로값) 지정
2. 해당 index의 맨 위 벽돌부터 연속적으로 다 깬다.
3. 빈 공간 없이 메워준다.
4. n회가 끝나면 남은 벽돌 수를 체크한다.
1. 구슬을 쏠 index(w, 가로값) 지정
DFS로 모든 경우를 다 따져줍니다.
2. 해당 index의 맨 위 벽돌부터 연속적으로 모두 깬다.
이때, 백트레킹이 가능하도록 임시 map인 tmap을 복사해줍니다.
2-1. for문으로 tmap[i][index]>0 인 경우를 찾아 start값으로 정합니다.
2-2. tmap[i][index]==1 인 경우, 해당 칸만 0으로 바꾸어줍니다.
2-3. tmap[i][index] > 1 인 경우, 그 이상인 경우는 범위만큼 모두 깨줘야하기 때문에 큐에 넣어줍니다. 범위 내 값들을 0으로 바꿉니다. 탐색 중 같은 경우가 발생 시, 큐에 넣어주는 것을 반복합니다. 이때 방문하는 tmap[x][y]은 모두 0으로 표기합니다.
3. 빈 공간 없이 매워준다.
tmap 전체를 height 기준으로 돌며 1 이상인 값들을 큐에 넣어줍니다. 그리고, h-1부터 차례대로 배열에 값들을 채워줍니다.
4. n회가 끝나면 남은 벽돌 수를 체크한다.
종료조건이 만족할 때까지 1,2,3을 반복하고 마지막에 벽돌 수를 체크합니다.
코드
#include <iostream>
#include <queue>
#include <cstring>
using namespace std;
int n, w, h;
int map[15][12];
int dx[] = { 1,-1,0,0 };
int dy[] = { 0,0,1,-1 };
int answer;
void dfs(int index, int cnt, int (*nmap)[12]) {
//4. n회가 끝나면 남은 벽돌 수를 체크한다.
if (cnt == n) {
int res = 0;
for (int i = 0; i < h; i++) {
for (int j = 0; j < w; j++) {
if (nmap[i][j] > 0) res++;
}
}
if (res < answer)answer = res;
return;
}
int tmap[15][12];
bool visited[15][12];
memset(visited, 0, sizeof(visited));
for (int i = 0; i < h; i++) {
for (int j = 0; j < w; j++) {
tmap[i][j] = nmap[i][j];
}
}
//2. 해당 index의 맨 위 벽돌부터 연속적으로 모두 깬다.
int start = -1;
//2-1. 맨 위 벽돌 찾기
for (int i = 0; i < h; i++) {
if (tmap[i][index] > 0) {
start = i;
break;
}
}
if (start >= 0) {
//2-2. 범위가 1인 경우
if (tmap[start][index] == 1) {
tmap[start][index] = 0;
}
//2-3. 범위가 1 이상인 경우
else {
queue<pair<int, int>> q;
q.push({ start,index });
visited[start][index] = true;
while (!q.empty()) {
int x = q.front().first;
int y = q.front().second;
q.pop();
//범위 체크 및 벽돌 깨기(0 채우기)
int d = tmap[x][y];
tmap[x][y] = 0;
for (int i = 0; i < 4; i++) {
int nx = x;
int ny = y;
for (int j = 0; j < d - 1; j++) {
nx += dx[i];
ny += dy[i];
if (nx < 0 || ny < 0 || nx >= h || ny >= w)break;
//깨야할 범위가 추가되는 경우, 큐에 넣기
if (tmap[nx][ny] > 1 && !visited[nx][ny]) {
visited[nx][ny] = true;
q.push({ nx,ny });
}
//범위가 1인 벽돌일 경우
else if (tmap[nx][ny] == 1 && !visited[nx][ny]) {
visited[nx][ny] = true;
tmap[nx][ny] = 0;
}
}
}
}
//3. 빈 공간 채우기
for (int i = 0; i < w; i++) {
queue<int> brick;
for (int j = h - 1; j >= 0; j--) {
if (tmap[j][i] > 0) {
brick.push(tmap[j][i]);
tmap[j][i] = 0;
}
}
int idx = h - 1;
while (!brick.empty()) {
if (idx >= 0) {
tmap[idx][i] = brick.front();
brick.pop();
idx--;
}
}
}
}
}
for (int i = 0; i < w; i++) {
dfs(i, cnt + 1, tmap);
}
}
int main() {
int T = 0;
cin >> T;
for (int t = 1; t <= T; t++) {
cin >> n >> w >> h;
answer = 987654321;
for (int i = 0; i < h; i++) {
for (int j = 0; j < w; j++) {
cin >> map[i][j];
}
}
//1. 구슬을 쏠 index(w, 가로값) 지정
for (int i = 0; i < w; i++) {
dfs(i, 0, map);
}
cout << "#" << t << " " << answer << endl;
}
}
반응형