14502번: 연구소
인체에 치명적인 바이러스를 연구하던 연구소에서 바이러스가 유출되었다. 다행히 바이러스는 아직 퍼지지 않았고, 바이러스의 확산을 막기 위해서 연구소에 벽을 세우려고 한다. 연구소는 크
www.acmicpc.net
문제
인체에 치명적인 바이러스를 연구하던 연구소에서 바이러스가 유출되었다. 다행히 바이러스는 아직 퍼지지 않았고,
바이러스의 확산을 막기 위해서 연구소에 벽을 세우려고 한다.
연구소는 크기가 N×M인 직사각형으로 나타낼 수 있으며, 직사각형은 1×1 크기의 정사각형으로 나누어져 있다.
연구소는 빈 칸, 벽으로 이루어져 있으며, 벽은 칸 하나를 가득 차지한다.
일부 칸은 바이러스가 존재하며, 이 바이러스는 상하좌우로 인접한 빈 칸으로 모두 퍼져나갈 수 있다.
새로 세울 수 있는 벽의 개수는 3개이며, 꼭 3개를 세워야 한다.
예를 들어, 아래와 같이 연구소가 생긴 경우를 살펴보자.
2 0 0 0 1 1 0
0 0 1 0 1 2 0
0 1 1 0 1 0 0
0 1 0 0 0 0 0
0 0 0 0 0 1 1
0 1 0 0 0 0 0
0 1 0 0 0 0 0
이때, 0은 빈 칸, 1은 벽, 2는 바이러스가 있는 곳이다. 아무런 벽을 세우지 않는다면, 바이러스는 모든 빈 칸으로 퍼져나갈 수 있다.
2행 1열, 1행 2열, 4행 6열에 벽을 세운다면 지도의 모양은 아래와 같아지게 된다.
2 1 0 0 1 1 0
1 0 1 0 1 2 0
0 1 1 0 1 0 0
0 1 0 0 0 1 0
0 0 0 0 0 1 1
0 1 0 0 0 0 0
0 1 0 0 0 0 0
바이러스가 퍼진 뒤의 모습은 아래와 같아진다.
2 1 0 0 1 1 2
1 0 1 0 1 2 2
0 1 1 0 1 2 2
0 1 0 0 0 1 2
0 0 0 0 0 1 1
0 1 0 0 0 0 0
0 1 0 0 0 0 0
벽을 3개 세운 뒤, 바이러스가 퍼질 수 없는 곳을 안전 영역이라고 한다. 위의 지도에서 안전 영역의 크기는 27이다.
연구소의 지도가 주어졌을 때 얻을 수 있는 안전 영역 크기의 최댓값을 구하는 프로그램을 작성하시오.
입력
첫째 줄에 지도의 세로 크기 N과 가로 크기 M이 주어진다. (3 ≤ N, M ≤ 8)
둘째 줄부터 N개의 줄에 지도의 모양이 주어진다. 0은 빈 칸, 1은 벽, 2는 바이러스가 있는 위치이다.
2의 개수는 2보다 크거나 같고, 10보다 작거나 같은 자연수이다.
빈 칸의 개수는 3개 이상이다.
출력
첫째 줄에 얻을 수 있는 안전 영역의 최대 크기를 출력한다.
문제풀이
위의 내용을 정리해보면
1. 연구소의 빈 칸 : 0, 벽 : 1, 바이러스 칸 : 2
2. 격자의 크기 N x M의 크기로 입력을 받는다 (3 <= N, M <=8)
3. 벽은 무조건 3개를 세워야 한다.
4. 바이러스 전염 : 2가 있는 칸 기준으로 (상,하,좌,우)에 0이면 2가 된다.
5. 안전 영역의 최대 크기를 구하라.
정리한 내용을 바탕으로 맨 처음 수행해야 할 것은 격자 크기 N, M을 입력 받는 것이다.
그 후 격자 크기에 맞게 입력 값을 입력받는다.
위의 과정을 끝낸 후 가장 중요한 벽을 3개 세우는 과정을 진행해야 한다.
이 과정이 제일 핵심인데 벽을 계속해서 옮겨가며 안전 영역의 최대 크기를 구해야하기 때문에,
DFS 방식으로 생성을 해주면 된다.
#include<stdio.h>
int answer; // 최종 값
int n, m; // 격자 크기
int arr[8][8];
// 벽 생성
void wall_dfs(int count, int start_x, int start_y) {
if (count == 3) {
//TODO something...
return;
}
for (int i = start_x; i < n; i++) {
for (int j = start_y; j < m; j++) {
if (arr[i][j] == 0) {
arr[i][j] = 1;
wall_dfs(count + 1, i, j);
arr[i][j] = 0;
}
}
start_y = 0;
}
}
int main() {
scanf("%d %d", &n, &m);
for (int i = 0; i < n; i++) {
for (int j = 0; j < m; j++) {
scanf("%d", &arr[i][j]);
}
}
wall_dfs(0, 0, 0);
printf("%d", answer);
return 0;
}
위의 코드가 3개의 벽을 세우며 모든 경우의 수를 훑어보는 과정을 만든 DFS 방식의 코드이다.
매개변수로 벽의 개수, 벽을 세우기 시작하는 최초 위치의 x(행) 좌표, y(열) 좌표를 넣어줬다.
무조건 3개의 벽을 세워야 하기 때문에 벽의 개수가 3개가 되었을 때 바이러스의 확산과 최대 값 갱신을 위해,
if 문으로 조건을 만들어주었고, for 문의 내용을 보면 i는 시작 x(행)위치, j는 시작 y(열)위치부터 격자 크기만큼 빈 칸(0)이 있는지
체크하고, 있다면 벽을 세우고 벽의 개수를 늘리고 재귀호출을 하여 2번의 벽, 3번의 벽을 같은 방식으로 세운다.
3개의 벽을 세우고 갱신이 끝나면 다음 벽으로 이동할 때 세웠던 벽은 다시 빈 칸(0) 으로 만들어준다.
그리고 만약 같은 행에 벽을 세울 공간이 없다면 다음 행으로 넘어가는데, (x, 0) 의 위치부터 다시 벽을 만들 수 있는
공간이 있는지 체크해야 하기 때문에 y(열)의 좌표 값을 0으로 만들어준 것이다.
최종코드1
#include<stdio.h>
int answer; // 최종 값
int n, m; // 격자 크기
int arr[8][8];
// 상하좌우
int dx[4] = { -1, 1, 0, 0 };
int dy[4] = { 0, 0, -1, 1 };
// 바이러스 확산
void virus() {
int arr_temp[8][8];
for (int i = 0; i < n; i++) {
for (int j = 0; j < m; j++) {
arr_temp[i][j] = arr[i][j];
}
}
// 벽이 소용돌이 처럼 되어있는 경우를 대비하여 여러번 확산 진행
for (int num = 0; num < 10; num++) {
for (int i = 0; i < n; i++) {
for (int j = 0; j < m; j++) {
// 바이러스가 존재한다면
if (arr_temp[i][j] == 2) {
for (int dir = 0; dir < 4; dir++) {
// 확산 가능 칸수 최대 7칸
for (int k = 1; k < 8; k++) {
int nx = i + dx[dir] * k;
int ny = j + dy[dir] * k;
// 격자 밖이거나, 1을 만나면 방향 전환
if (nx < 0 || nx >= n || ny < 0 || ny >= m || arr_temp[nx][ny] == 1) break;
else arr_temp[nx][ny] = 2;
}
}
}
}
}
}
// 안전 영역 최댓값 갱신
int sum = 0;
for (int i = 0; i < n; i++) {
for (int j = 0; j < m; j++) {
if (arr_temp[i][j] == 0) sum += 1;
}
}
if (answer < sum) {
answer = sum;
}
}
// 벽 생성
void wall_dfs(int count, int start_x, int start_y) {
if (count == 3) {
virus();
return;
}
for (int i = start_x; i < n; i++) {
for (int j = start_y; j < m; j++) {
if (arr[i][j] == 0) {
arr[i][j] = 1;
wall_dfs(count + 1, i, j);
arr[i][j] = 0;
}
}
start_y = 0;
}
}
int main() {
scanf("%d %d", &n, &m);
for (int i = 0; i < n; i++) {
for (int j = 0; j < m; j++) {
scanf("%d", &arr[i][j]);
}
}
wall_dfs(0, 0, 0);
printf("%d", answer);
return 0;
}
위의 방식은 큐(queue) 방식을 활용하지 않고 바이러스 전염을 진행한 것이다.
확산은 바이러스가 있는 칸에서 상,하,좌,우에 존재하는 0칸 즉 벽(1)의 방해가 없을 때까지 한번에 감염을 시켰다.
하지만 이런 방식으로 (0, 0)부터 (N - 1, M - 1)까지 감염을 한번만 수행하게 된다면, 빈칸(0)이 바이러스(2)의
옆 칸임에도 감염이 안돼 있는 경우가 발생한다. 따라서 여러번의 반복 수행을 통해 감염을 확실하게 시킨다.
아래의 방식은 큐(queue) 방식을 활용해서 바이러스 전염을 진행한 것이다.
큐 방식을 활용해서 진행하게 된다면, 더욱 빠르고 확실하게 감염을 끝낼 수 있다.
큐 1차원 배열에는 바이러스의 위치를 담고, 추가 설명은 주석으로 담겨 있다.
최종코드2
#include<stdio.h>
int answer; // 최종 값
int n, m; // 격자 크기
int arr[8][8];
// 상하좌우
int dx[4] = { -1, 1, 0, 0 };
int dy[4] = { 0, 0, -1, 1 };
#define QUEUESIZE 100
int queue[QUEUESIZE];
int queue_size = 0;
// 큐 맨 뒤에 데이터 삽입
void push(int x) {
queue[queue_size] = x;
queue_size++;
return;
}
// 큐 맨 앞 데이터 삭제
void pop() {
if (queue_size == 0) {
return;
}
for (int i = 0; i < queue_size; i++) {
queue[i] = queue[i + 1];
}
queue_size -= 1;
}
// 바이러스 확산
void virus() {
int arr_temp[8][8];
for (int i = 0; i < n; i++) {
for (int j = 0; j < m; j++) {
arr_temp[i][j] = arr[i][j];
if (arr_temp[i][j] == 2) push(i * 10 + j);
// 큐에 바이러스 위치 담기 i(행), j(열)
// ex. i = 3, j = 2 -> push(32)
// 값을 꺼낼 때 x(행)은 나누기 10 해주면 3이 나옴
// y(열)은 퍼센트(%) 10 해주면 2가 나옴
}
}
// 큐를 활용하여 바이러스 전염
while (queue_size != 0) {
// 바이러스 위치
int nx = queue[0] / 10;
int ny = queue[0] % 10;
pop();
// 상,하,좌,우 0이면 2로 감염
for (int dir = 0; dir < 4; dir++) {
int px = nx + dx[dir];
int py = ny + dy[dir];
// 격자 밖이거나 0이 아니면
if (px < 0 || px >= n || py < 0 || py >= m || arr_temp[px][py] != 0) continue;
arr_temp[px][py] = 2;
push(px * 10 + py);
}
}
// 안전 영역 최댓값 갱신
int sum = 0;
for (int i = 0; i < n; i++) {
for (int j = 0; j < m; j++) {
if (arr_temp[i][j] == 0) sum += 1;
}
}
if (answer < sum) {
answer = sum;
}
}
// 벽 생성
void wall_dfs(int count, int start_x, int start_y) {
if (count == 3) {
virus();
return;
}
for (int i = start_x; i < n; i++) {
for (int j = start_y; j < m; j++) {
if (arr[i][j] == 0) {
arr[i][j] = 1;
wall_dfs(count + 1, i, j);
arr[i][j] = 0;
}
}
start_y = 0;
}
}
int main() {
scanf("%d %d", &n, &m);
for (int i = 0; i < n; i++) {
for (int j = 0; j < m; j++) {
scanf("%d", &arr[i][j]);
}
}
wall_dfs(0, 0, 0);
printf("%d", answer);
return 0;
}
'삼성 SW 역량 테스트 기출 문제' 카테고리의 다른 글
백준 14501: 퇴사(C언어) (0) | 2023.02.01 |
---|---|
백준 14890: 경사로(C언어) (0) | 2023.01.27 |
백준 13460: 구슬탈출2(C언어) (1) | 2023.01.26 |
백준 19236: 청소년 상어(C언어) (1) | 2023.01.15 |
백준 20057: 마법사 상어와 토네이도(C언어) (0) | 2023.01.10 |
댓글