# BOJ 15683 감시

Wonder_Why (Today I learned)·2022년 2월 7일
0

BOJ

목록 보기
56/70
post-thumbnail

🏢감시

(https://www.acmicpc.net/problem/15683)

시간 제한	메모리 제한	제출	정답	맞힌 사람	정답 비율
1 초	512 MB	29046	13339	7889	42.613%

문제
스타트링크의 사무실은 1×1크기의 정사각형으로 나누어져 있는 N×M 크기의 직사각형으로 나타낼 수 있다. 사무실에는 총 K개의 CCTV가 설치되어져 있는데, CCTV는 5가지 종류가 있다. 각 CCTV가 감시할 수 있는 방법은 다음과 같다.

1번 CCTV는 한 쪽 방향만 감시할 수 있다. 2번과 3번은 두 방향을 감시할 수 있는데, 2번은 감시하는 방향이 서로 반대방향이어야 하고, 3번은 직각 방향이어야 한다. 4번은 세 방향, 5번은 네 방향을 감시할 수 있다.

CCTV는 감시할 수 있는 방향에 있는 칸 전체를 감시할 수 있다. 사무실에는 벽이 있는데, CCTV는 벽을 통과할 수 없다. CCTV가 감시할 수 없는 영역은 사각지대라고 한다.

CCTV는 회전시킬 수 있는데, 회전은 항상 90도 방향으로 해야 하며, 감시하려고 하는 방향이 가로 또는 세로 방향이어야 한다.

0 0 0 0 0 0
0 0 0 0 0 0
0 0 1 0 6 0
0 0 0 0 0 0

지도에서 0은 빈 칸, 6은 벽, 1~5는 CCTV의 번호이다. 위의 예시에서 1번의 방향에 따라 감시할 수 있는 영역을 '#'로 나타내면 아래와 같다.

0 0 0 0 0 0
0 0 0 0 0 0
0 0 1 # 6 0
0 0 0 0 0 0
0 0 0 0 0 0
0 0 0 0 0 0

CCTV는 벽을 통과할 수 없기 때문에, 1번이 → 방향을 감시하고 있을 때는 6의 오른쪽에 있는 칸을 감시할 수 없다.

(중략 ... 전체 문제는 :https://www.acmicpc.net/problem/15683 )

사무실의 크기와 상태, 그리고 CCTV의 정보가 주어졌을 때, CCTV의 방향을 적절히 정해서, 사각 지대의 최소 크기를 구하는 프로그램을 작성하시오.

입력

첫째 줄에 사무실의 세로 크기 N과 가로 크기 M이 주어진다. (1 ≤ N, M ≤ 8)
둘째 줄부터 N개의 줄에는 사무실 각 칸의 정보가 주어진다. 0은 빈 칸, 6은 벽, 1~5는 CCTV를 나타내고, 문제에서 설명한 CCTV의 종류이다.
CCTV의 최대 개수는 8개를 넘지 않는다.

출력

첫째 줄에 사각 지대의 최소 크기를 출력한다.

예제 입력 1
4 6
0 0 0 0 0 0
0 0 0 0 0 0
0 0 1 0 6 0
0 0 0 0 0 0

예제 출력 1
20

예제 입력 2
6 6
0 0 0 0 0 0
0 2 0 0 0 0
0 0 0 0 6 0
0 6 0 0 2 0
0 0 0 0 0 0
0 0 0 0 0 5

예제 출력 2
15

예제 입력 3
6 6
1 0 0 0 0 0
0 1 0 0 0 0
0 0 1 0 0 0
0 0 0 1 0 0
0 0 0 0 1 0
0 0 0 0 0 1

예제 출력 3
6

예제 입력 4
6 6
1 0 0 0 0 0
0 1 0 0 0 0
0 0 1 5 0 0
0 0 5 1 0 0
0 0 0 0 1 0
0 0 0 0 0 1

예제 출력 4
2

예제 입력 5
1 7
0 1 2 3 4 5 6

예제 출력 5
0

예제 입력 6
3 7
4 0 0 0 0 0 0
0 0 0 2 0 0 0
0 0 0 0 0 0 4

예제 출력 6
0


풀이

일단, 해당 문제의 시간 제한은 1초이고 메모리 제한은 512MB 이다.

메모리는 넉넉한 편이니 큰 신경을 쓰지 않아도 되겠다.

CCTV 종류는 5가지가 있고, 각각 4번의 회전이 가능하다.

CCTV는 최대 8 개를 사용할 수 있다.

CCTV 의 개수에 따라서 경우의 수가 다를 것이다.

최악의 경우의 수를 생각해보면

CCTV 가 8개 주어진 경우일 것이다.

  • CCTV 회전해가며 모든 경우의 수를 계산해보면 65,536 개로 상당히 적다. 그러므로 모든 경우의 수를 탐색하여도 시간 제한 조건을 넉넉히 통과할 것으로 보인다.

전략 수립
1. CCTV의 개수를 파악
2. CCTV를 회전시키는 경우의 수를 모두 탐색하며 사각지대의 최솟값을 계산



생각해보면 모든 CCTV가 4번을 회전할 필요는 없다.

TYPE 2 의 경우, 90도 방향으로 회전시키는 모습을 상상해보자.

사실 2가지 회전에 대해서는 중복이 된다.

TYPE 5 의 경우도 마찬가지다.

4번 회전을 시키든, 시키지 않든 같은 감시 영역을 가진다.

그말은 즉슨, TYPE 5는 회전을 시키는 것은 무의미한 연산이 되겠다.

이러한 특성을 TYPE 별로 유의미한 회전 가짓수 배열에 저장해놓자.

int rot_size[ ]{ 4,2,4,4,1 };

CCTV의 정보를 보다 쉽게 처리하기 위해 구조체를 선언하자.

 struct CCTV
{
	int x;
	int y;
	int type;
};

CCTV cctv[8];
int cctv_size = 0;

cctv는 최대 8개를 가질 수 있으므로 CCTV 구조체 배열의 크기를 8로 설정하였고,

cctv_size 는 CCTV의 개수를 나타내는데 일단 0으로 초기화하였다.

이제 메인 함수를 구성해보자.

열과 행 그리고 사무실 정보를 2차원 배열로 받아야 한다.

사무실 정보를 받을 때, CCTV를 입력 받는 경우 이를 따로 저장해주어야 한다.

코드 내에서 cctv_type 을 graph[i][j] - 1 로 받은 이유는 type 인덱싱을 0~4 로 하기 위함이다.

	ios::sync_with_stdio(0); cin.tie(0);
	cin >> row >> col;
	for (int i = 0; i < row; i++)
	{
		for (int j = 0; j < col; j++)
		{
			cin >> graph[i][j];
			if (graph[i][j] != 0 && graph[i][j] != 6)
			{
				cctv[cctv_size].x = i;
				cctv[cctv_size].y = j;
				cctv[cctv_size].type = graph[i][j] - 1;
				cctv_size++;
			}
		}
	}

이렇게 입력을 받으면 사무실과 cctv 정보를 저장할 수 있다.

이제 백트래킹을 이용하여 모든 경우의 수를 돌며 최소의 사각지대를 출력해주면 되겠다.

int main()
{
	... 생략 ...
    
   	answer = 100; // 비교하기 위함
	solve(0);
	cout << answer;

}

answer 은 비교를 하기위해 초깃값을 100으로 설정하였다.

안전구역의 개수는 아무리 많아도 100을 초과할 수 없으므로 이와 같이 정하였다.

이제 solve 함수를 구성해보자.

void solve (int k)
{
	if( cctv의 개수 만큼 모든 경우를 다 따져보았다면 )
    {
    	1. 안전구역의 개수를 찾는다.
        2. 이전 값과 비교하여 더 작다면, answer 에 저장한다.
        3. return;
    }
    
    1. 백트래킹을 위해 graph 를 backup 시킨다.
    for( 현재 인덱스(k)의 cctv 의 회전 경우의 수동안 반복한다. )
    {
    	1. graph 를 backup 에 저장한다.
        if( type == 0 이라면)
        	{ TYPE 0 의 감시 방향에만 graph를 -1로 칠한다.}
        if( type == 1 이라면)
        	{ TYPE 1의 감시 방향에만 graph를 -1로 칠한다.}
   		if( type == 2 이라면)
        	{ TYPE 2의 감시 방향에만 graph를 -1로 칠한다.}
            ...
            ...
		if( type == 4 이라면)
        	{ TYPE 4의 감시 방향에만 graph를 -1로 칠한다.}
    	2. 하나의 CCTV의 경우의 수에 대해 수행했으므로 CCTV 를 하나 늘려서 재탐색 solve(k+1);
        3. 원상복구
    }

}

이를 코드로 구현하면,

void solve(int k)
{
	if (k == cctv_size)
	{
		int safe_area = 0;
		for (int i = 0; i < row; i++)
		{
			for (int j = 0; j < col; j ++ )
			{
				if (graph[i][j] == 0) safe_area++;
			}
		}
		answer = min(safe_area, answer);
		return;
	}
	
	int backup[8][8];
	int type = cctv[k].type;
	for (int dir = 0; dir < rot_size[type]; dir++)
	{
		copy(backup, graph);
		
		if (type == 0)
		{
			update(dir, cctv[k]);
		}
		if (type == 1)
		{
			update(dir, cctv[k]);
			update(dir+2, cctv[k]);
		}
		if (type == 2)
		{
			update(dir, cctv[k]);
			update(dir+1, cctv[k]);
		}
		if (type == 3)
		{
			update(dir, cctv[k]);
			update(dir+1, cctv[k]);
			update(dir+2, cctv[k]);
		}
		if (type == 4)
		{
			update(dir, cctv[k]);
			update(dir+1, cctv[k]);
			update(dir+2, cctv[k]);
			update(dir+3, cctv[k]);

		}

		solve(k + 1);
		copy(graph, backup);
	}

}

이고, solve 를 위해 따로 만든 update 함수와 copy 함수는 아래와 같다.

void update(int dir, CCTV c)
{
	dir = dir % 4;
	if (dir == 0) // 동
	{
		for (int y = c.y + 1; y< col; y++)
		{
			if (graph[c.x][y] == 6) break;
			graph[c.x][y] = -1;
		}
	}
	if (dir == 1) // 북
	{
		for (int x = c.x - 1; x >= 0; x--)
		{
			if (graph[x][c.y] == 6) break;
			graph[x][c.y] = -1;
		}
	}
	if (dir == 2) // 서
	{
		for (int y = c.y - 1; y >=0; y--)
		{
			if (graph[c.x][y] == 6) break;
			graph[c.x][y] = -1;
		}
	}
	if (dir == 3) // 남
	{
		for (int x = c.x + 1; x < row ; x++)
		{
			if (graph[x][c.y] == 6) break;
			graph[x][c.y] = -1;
		}
	}

}
void copy(int desc[8][8], int src[8][8])
{
	for (int i = 0; i < row; i++)
	{
		for (int j = 0; j < col; j++)
		{
			desc[i][j] = src[i][j];
		}
	}
}


전체 코드

/*
BOJ : https://www.acmicpc.net/problem/15683
backtracking 감시 ( 삼성 SW 역량 평가 )
Versatile0010
*/

#include <bits/stdc++.h>
using namespace std;
int row, col;
int graph[8][8]; // 사무실 정보 입력

struct CCTV
{
	int x;
	int y;
	int type;
};

CCTV cctv[8];
int cctv_size = 0;

int answer;
int rot_size[]{ 4,2,4,4,1 };

void copy(int desc[8][8], int src[8][8])
{
	for (int i = 0; i < row; i++)
	{
		for (int j = 0; j < col; j++)
		{
			desc[i][j] = src[i][j];
		}
	}
}

void update(int dir, CCTV c)
{
	dir = dir % 4;
	if (dir == 0) // 동
	{
		for (int y = c.y + 1; y< col; y++)
		{
			if (graph[c.x][y] == 6) break;
			graph[c.x][y] = -1;
		}
	}
	if (dir == 1) // 북
	{
		for (int x = c.x - 1; x >= 0; x--)
		{
			if (graph[x][c.y] == 6) break;
			graph[x][c.y] = -1;
		}
	}
	if (dir == 2) // 서
	{
		for (int y = c.y - 1; y >=0; y--)
		{
			if (graph[c.x][y] == 6) break;
			graph[c.x][y] = -1;
		}
	}
	if (dir == 3) // 남
	{
		for (int x = c.x + 1; x < row ; x++)
		{
			if (graph[x][c.y] == 6) break;
			graph[x][c.y] = -1;
		}
	}

}


void solve(int k)
{
	if (k == cctv_size)
	{
		int safe_area = 0;
		for (int i = 0; i < row; i++)
		{
			for (int j = 0; j < col; j ++ )
			{
				if (graph[i][j] == 0) safe_area++;
			}
		}
		answer = min(safe_area, answer);
		return;
	}
	
	int backup[8][8];
	int type = cctv[k].type;
	for (int dir = 0; dir < rot_size[type]; dir++)
	{
		copy(backup, graph);
		
		if (type == 0)
		{
			update(dir, cctv[k]);
		}
		if (type == 1)
		{
			update(dir, cctv[k]);
			update(dir+2, cctv[k]);
		}
		if (type == 2)
		{
			update(dir, cctv[k]);
			update(dir+1, cctv[k]);
		}
		if (type == 3)
		{
			update(dir, cctv[k]);
			update(dir+1, cctv[k]);
			update(dir+2, cctv[k]);
		}
		if (type == 4)
		{
			update(dir, cctv[k]);
			update(dir+1, cctv[k]);
			update(dir+2, cctv[k]);
			update(dir+3, cctv[k]);

		}

		solve(k + 1);
		copy(graph, backup);
	}

}

int main()
{
	ios::sync_with_stdio(0); cin.tie(0);
	cin >> row >> col;
	for (int i = 0; i < row; i++)
	{
		for (int j = 0; j < col; j++)
		{
			cin >> graph[i][j];
			if (graph[i][j] != 0 && graph[i][j] != 6)
			{
				cctv[cctv_size].x = i;
				cctv[cctv_size].y = j;
				cctv[cctv_size].type = graph[i][j] - 1;
				cctv_size++;
			}
		}
	}
	answer = 100;
	solve(0);
	cout << answer;

	return 0;
}

GIT : https://github.com/versatile0010/Algorithm/blob/main/Simulation/BOJ%2015683%20%EA%B0%90%EC%8B%9C.cpp

profile
전자과 머학생

0개의 댓글