-
[C++] BOJ 15683번: 감시프로그래밍/알고리즘 PS 2020. 10. 20. 10:11
문제 설명
총 5종류의 CCTV가 문제에서 주어지고, CCTV의 배치도가 주어졌을 때 CCTV 사각지대의 최소 개수를 구하는 문제이다.
M, N이 8 이하로 작기 때문에 백트래킹을 이용하여 문제를 풀어보았다. DFS 문제를 풀 때와 비슷하게 dx, dy 상수를 만들어 이동 방향을 설정해주었다.
문제를 읽을 때 "CCTV는 CCTV를 통과할 수 있다." 라는 부분을 읽지 못하여 여러 번 틀리면서 제출하였다. 문제를 주의 깊게 읽을 필요성을 느꼈다.
코드가 많이 길어졌는데 더 간결하게 작성할 방법을 찾아볼 필요가 있어보인다.
코드
#include <iostream> #include <vector> #include <utility> #include <queue> // 문제에서 제시한 0~6의 숫자 #define EMPTY 0 #define WALL 6 #define ONE 1 #define TWO 2 #define THREE 3 #define FOUR 4 #define FIVE 5 // CCTV가 한 번 이상 본 위치는 WATCHED 이상의 값으로 설정함 #define WATCHED 7 using namespace std; int N, M; int map[12][12]; int emptycnt = 0; vector<pair<int, int>> cctvpos; vector<int> input; // 초기의 result 값을 M * N보다 큰 값으로 두었음. 이 코드에서 실제로 9999라는 값이 출력되는 일은 없음. int result = 9999; // 이동 방향 설정 const int dx[4] = {1, 0, -1, 0}; const int dy[4] = {0, 1, 0, -1}; void checkStraightLine(pair<int, int> &pos, int dir) { int x = pos.first; int y = pos.second; while (true) { x += dx[dir]; y += dy[dir]; if (x < 0 || y < 0 || x >= M || y >= N) // (0,0)부터 (M-1,N-1) 사이를 넘어선 영역에 도달하면 break break; if (map[x][y] == WALL) // 벽에 가로막히면 break break; if (ONE <= map[x][y] && map[x][y] <= FIVE) continue; // CCTV는 다른 CCTV 너머를 볼 수 있기 때문에 break 대신 continue 사용. if (map[x][y] >= WATCHED) // 이미 다른 CCTV가 본 곳을 중복해서 보았을 경우 해당 위치의 값을 1 증가시킴. { map[x][y] += 1; } else if (map[x][y] == EMPTY) // 아직 다른 CCTV가 보지 않은 EMPTY일 경우 해당 위치의 값을 WATCHED로 바꿈. { map[x][y] = WATCHED; emptycnt--; // EMPTY인 공간이 1칸 줄어들었으므로 emptycnt 값 1 감소. } } } void uncheckStraightLine(pair<int, int> &pos, int dir) { int x = pos.first; int y = pos.second; while (true) { x += dx[dir]; y += dy[dir]; if (x < 0 || y < 0 || x >= M || y >= N) // (0,0)부터 (M-1,N-1) 사이를 넘어선 영역에 도달하면 break break; if (map[x][y] == WALL) // 벽에 가로막히면 break break; if (ONE <= map[x][y] && map[x][y] <= FIVE) // CCTV는 다른 CCTV 너머를 볼 수 있기 때문에 break 대신 continue 사용. continue; if (map[x][y] > WATCHED) // 이미 다른 CCTV가 중복해서 본 곳이므로 값을 1 감소시킴. { map[x][y] -= 1; } else if (map[x][y] == WATCHED) { map[x][y] = EMPTY; emptycnt++; // EMPTY인 공간이 1칸 늘어났으므로 emptycnt 값 1 증가. } } } // dir을 이용하여 각도를 표현함. 0은 0도, 1은 90도, 2는 180도, 3은 270도 회전을 의미. void checkOne(pair<int, int> &pos, int dir) { checkStraightLine(pos, dir); } void checkTwo(pair<int, int> &pos, int dir) { checkStraightLine(pos, dir); checkStraightLine(pos, (dir + 2) % 4); } void checkThree(pair<int, int> &pos, int dir) { checkStraightLine(pos, dir); checkStraightLine(pos, (dir + 1) % 4); } void checkFour(pair<int, int> &pos, int dir) { checkStraightLine(pos, dir); checkStraightLine(pos, (dir + 1) % 4); checkStraightLine(pos, (dir + 2) % 4); } void checkFive(pair<int, int> &pos) { checkStraightLine(pos, 0); checkStraightLine(pos, 1); checkStraightLine(pos, 2); checkStraightLine(pos, 3); } void uncheckOne(pair<int, int> &pos, int dir) { uncheckStraightLine(pos, dir); } void uncheckTwo(pair<int, int> &pos, int dir) { uncheckStraightLine(pos, dir); uncheckStraightLine(pos, (dir + 2) % 4); } void uncheckThree(pair<int, int> &pos, int dir) { uncheckStraightLine(pos, dir); uncheckStraightLine(pos, (dir + 1) % 4); } void uncheckFour(pair<int, int> &pos, int dir) { uncheckStraightLine(pos, dir); uncheckStraightLine(pos, (dir + 1) % 4); uncheckStraightLine(pos, (dir + 2) % 4); } void uncheckFive(pair<int, int> &pos) { uncheckStraightLine(pos, 0); uncheckStraightLine(pos, 1); uncheckStraightLine(pos, 2); uncheckStraightLine(pos, 3); } void calcArea(int depth) { if (depth >= input.size()) { if (emptycnt < result) { result = emptycnt; } return; } switch (input[depth]) { case ONE: for (int i = 0; i < 4; i++) { checkOne(cctvpos[depth], i); calcArea(depth + 1); uncheckOne(cctvpos[depth], i); } break; case TWO: for (int i = 0; i < 2; i++) { checkTwo(cctvpos[depth], i); calcArea(depth + 1); uncheckTwo(cctvpos[depth], i); } break; case THREE: for (int i = 0; i < 4; i++) { checkThree(cctvpos[depth], i); calcArea(depth + 1); uncheckThree(cctvpos[depth], i); } break; case FOUR: for (int i = 0; i < 4; i++) { checkFour(cctvpos[depth], i); calcArea(depth + 1); uncheckFour(cctvpos[depth], i); } break; case FIVE: checkFive(cctvpos[depth]); calcArea(depth + 1); uncheckFive(cctvpos[depth]); } } int main(void) { ios::sync_with_stdio(false); cin.tie(nullptr); cin >> N >> M; for (int i = 0; i < N; i++) { for (int j = 0; j < M; j++) { cin >> map[j][i]; if (map[j][i] != EMPTY && map[j][i] != WALL) { cctvpos.push_back({j, i}); input.push_back(map[j][i]); } if (map[j][i] == EMPTY) { emptycnt++; } } } calcArea(0); cout << result << '\n'; return 0; }
'프로그래밍 > 알고리즘 PS' 카테고리의 다른 글
[C++] BOJ 14891번: 톱니바퀴 (0) 2020.11.13 [C++] BOJ 14499번: 주사위 굴리기 (0) 2020.11.04 [C++] BOJ 15686번: 치킨 배달 (0) 2020.10.29 [C++] BOJ 12100번: 2048 (Easy) (0) 2020.10.28 [C++] BOJ 18808번: 스티커 붙이기 (0) 2020.10.24