제한은 1초 512MB
맵의 크기는 8*8, 카메라를 4방향으로 돌릴 수 있고 카메라의 최대 갯수는 8개입니다.
카메라 8대 각각의 방향을 한 번씩 바꿔볼 때 나올 수 있는 경우의 수는 4^8 = 2^16 = 64k 번입니다. 카메라 각각은 고유함으로 중복은 없습니다. 각 경우에 사각 지역을 찾는 것은 8*8로 대충 100입니다. 64k * 100 = 6.4m 번입니다. 대충 1g 번 명령을 수행할 때 1초가 걸린다고 계산하면 6.4m 번은 0.01초(10ms) 이내에 수행될 수 있다고 생각합니다. 물론 카메라의 감시 영역을 체크하면서 수행될 명령문은 생각하지 않았지만 이를 차치하고서라도 시간은 충분해보입니다.
중복 순열은 재귀문으로 간단하게 작성할 수 있습니다.
void 중복순열(int 카메라 번호) { /* 최대 카메라 갯수 = 8 */
if 모든 카메라의 방향을 조정 ?
맵의 사각 지역을 카운트
return;
for (감시 방향) /* 방향 = 4 */
감시 체크
중복순열(이번 카메라 번호 + 1) // 다음 카메라로 넘어감
감시 해제
}
다음은 감시 체크와 해제를 하는 부분입니다. 우선 한 지점으로부터 한 방향으로 인자에 따라 감시 체크나 해제를 하는 함수를 만들었습니다. 감시 체크시에는 맵에서 -1을 하고 해제시엔 +1을 했습니다. 음의 정수의 크기로 중복 감시되는 지역을 표현했습니다.
int check(int x, int y) {
if (x < 0 || N <= x || y < 0 || M <= y || map[x][y] == 6) return -1;
if (0 < map[x][y] && map[x][y] < 6) return 0;
return 1;
}
void observe(int x, int y, int d, bool f) { // x,y 지점으로부터 d 방향으로 f (감시or해제)합니다.
int xx = x + dir[d][0];
int yy = y + dir[d][1];
int c = check(xx, yy);
while (c >= 0) {
if (c) {
map[xx][yy] += f ? -1 : 1;
}
xx += dir[d][0];
yy += dir[d][1];
c = check(xx, yy);
}
}
카메라 특성에 맞게 observe() 함수를 호출할 수 있도록 각 카메라에 대한 정보를 배열에 담아 참조하도록 했습니다.
int dir[4][2] = { {0, 1}, {0, -1}, {1, 0}, {-1, 0} };
int spec[5/*cctv*/][5/*time*/][4/*direction*/] = {
{ {1, 4}, {0}, {1}, {2}, {3} },
{ {2, 2}, {0, 1}, {2, 3}, {}, {} },
{ {2, 4}, {0, 3}, {0, 2}, {1, 2}, {1, 3} },
{ {3, 4}, {0, 1, 2}, {0, 1, 3}, {0, 2, 3}, {1, 2, 3} },
{ {4, 1}, {0, 1, 2, 3}, {}, {}, {}}
};
3차원 배열이며 첫 번째는 카메라의 번호를 뜻합니다. 1번 카메라에 대한 정보는 spec[0] 번에 저장됩니다. 두 번째는 카메라 회전 횟수를 의미합니다. 동서남북 방향으로 4회가 최대입니다. 하지만, 특정 카메라의 경우엔 네 방향 모두 돌릴 필요가 없습니다. 예를 들어, 2번 카메라의 경우엔 가로, 세로 2번만 회전시키면 됩니다. 카메라마다 필요한 감시 횟수가 다름으로 이를 두 번째의 0번째 공간을 활용해 표현했습니다. 예를 들어 spec[0][0], {1, 4} 는 1번 카메라가 한 번에 1방향만 탐색하고 4회 회전해야함을 의미합니다. 마지막은 확인해야할 방향을 의미합니다. dir[][] 배열을 이용해서 0~3의 정수로 동서남북을 표현하였습니다.
전체 코드입니다.
#pragma warning(disable: 4996)
#include <stdio.h>
int N, M, map[10][10], ans = 0x7fffffff;
int cctv[10][2], K;
int dir[4][2] = { {0, 1}, {0, -1}, {1, 0}, {-1, 0} };
int spec[5/*cctv*/][5/*time*/][4/*direction*/] = {
{ {1, 4}, {0}, {1}, {2}, {3} },
{ {2, 2}, {0, 1}, {2, 3}, {}, {} },
{ {2, 4}, {0, 3}, {0, 2}, {1, 2}, {1, 3} },
{ {3, 4}, {0, 1, 2}, {0, 1, 3}, {0, 2, 3}, {1, 2, 3} },
{ {4, 1}, {0, 1, 2, 3}, {}, {}, {}}
};
int check(int x, int y) {
if (x < 0 || N <= x || y < 0 || M <= y || map[x][y] == 6) return -1;
if (0 < map[x][y] && map[x][y] < 6) return 0;
return 1;
}
void observe(int x, int y, int d, bool f) {
int xx = x + dir[d][0];
int yy = y + dir[d][1];
int c = check(xx, yy);
while (c >= 0) {
if (c) {
map[xx][yy] += f ? -1 : 1;
}
xx += dir[d][0];
yy += dir[d][1];
c = check(xx, yy);
}
}
void solve(int cctvn) {
if (cctvn >= K) {
int area = 0;
for (int i = 0; i < N; i++)
for (int j = 0; j < M; j++)
area += (map[i][j] == 0);
if (area < ans)
ans = area;
return;
}
const int& x = cctv[cctvn][0];
const int& y = cctv[cctvn][1];
const int& cctvkind = map[x][y];
const int& filltime = spec[cctvkind - 1][0][1];
const int& fillcount = spec[cctvkind - 1][0][0];
for (int i = 0; i < filltime; i++) {
for (int j = 0; j < fillcount; j++)
observe(x, y, spec[cctvkind - 1][i + 1][j], true);
solve(cctvn + 1);
for (int j = 0; j < fillcount; j++)
observe(x, y, spec[cctvkind - 1][i + 1][j], false);
}
}
int main(void) {
scanf("%d %d", &N, &M);
for (int i = 0; i < N; i++)
for (int j = 0; j < M; j++) {
scanf("%d", &map[i][j]);
if (0 < map[i][j] && map[i][j] < 6)
cctv[K][0] = i, cctv[K++][1] = j;
}
solve(0);
printf("%d\n", ans);
return 0;
}
다른 분들의 코드에선 5번 카메라는 회전에 관계없이 고정이므로 아예 처음에 감시 체크를 하고 중복 순열에서 제외시키는 모습을 볼 수 있었습니다. 확실히 5번은 회전에 따라 감시 해제할 필요가 없죠.
또, 각 카메라의 회전 방향만 별도의 배열에 저장하고 넘어간 뒤 모든 카메라의 회전 방향을 지정했을 때(중복 순열의 마지막에) 그 배열을 참조해서 감시 체크를 하고 답을 구하는 식의 코드도 있었습니다.
'개발 > c' 카테고리의 다른 글
[번역][gstreamer] Tutorials을 시작하며... (0) | 2022.01.10 |
---|---|
[gstreamer] windows에서 설치 및 예제 빌드 (0) | 2022.01.04 |
[백준] 16236 아기 상어 (0) | 2020.04.28 |
[백준] 17825 주사위 윷놀이 (0) | 2020.04.24 |
[백준] 17822 원판 돌리기 (0) | 2020.04.22 |