https://www.acmicpc.net/problem/15683
호흡이 길었던 문제라서 따로 정리해보려 한다.
그래프 그림이 많지만 핵심적인 정보만 추려보자.
문제에서 추출할 수 있는 정보는 다음과 같다.
크게 3가지로 나눌 수 있다.
먼저 백트래킹은 아래와 같다.
void dfs(int idx, int cnt){
if(cnt == pos.size()){
cctv();
return;
}
for(int j=0; j<4; j++){
dir.push_back(j);
dfs(idx+1, cnt+1);
dir.pop_back();
}
}
다음으로 BFS
void copy(){
for(int i=0; i<n; i++){
for(int j=0; j<m; j++){
tmp[i][j] = map[i][j];
}
}
}
void cctv(){
copy();
for(int i=0; i<pos.size(); i++){
int nowdir = dir[i]; // i번째 CCTV의 방향(0~3)
int type = map[pos[i].first][pos[i].second]; // CCTV의 타입(1~5)
activate(nowdir, type, pos[i]);
}
int cnt = 0;
for(int i=0; i<n; i++){
for(int j=0; j<m; j++){
if(tmp[i][j] == 0){
cnt++;
}
}
}
answer = min(answer, cnt);
}
나는 tmp라는 임시 2차원 배열을 매 케이스마다 선언하여, 감시당하는 빈칸을 배열 내에서 일반 빈칸인 0과 차별화하기 위해 -1로 표현해주었다.
이후 activate 함수를 통해 CCTV를 활성화하고 그 결과 남은 0의 총 갯수를 계산하여 최솟값을 갱신하였다.
void draw(int x, int y, int dir){
while(1){
int nx = x + dx[dir];
int ny = y + dy[dir];
if(nx < 0 || ny < 0 || nx >= n || ny >= m || tmp[nx][ny] == 6)
break;
if(tmp[nx][ny] == 0){
tmp[nx][ny] = -1;
}
x = nx;
y = ny;
}
}
void activate(int dir, int type, pair<int, int> pos){
int x = pos.first;
int y = pos.second;
if(type == 1){
draw(x, y, dir);
} else if(type == 2){
draw(x, y, dir);
draw(x, y, (dir+2)%4);
} else if(type == 3){
draw(x, y, dir);
draw(x, y, (dir+1)%4);
} else if(type == 4){
draw(x, y, dir);
draw(x, y, (dir+1)%4);
draw(x, y, (dir+2)%4);
} else {
draw(x, y, dir);
draw(x, y, (dir+1)%4);
draw(x, y, (dir+2)%4);
draw(x, y, (dir+3)%4);
}
}
나는 while문을 통해 CCTV의 감시를 구현하였으며, 앞서 언급한 대로 감시당하는 빈칸은 -1로 처리했다.
개인적으로 호흡이 꽤 길어 코드를 고치는 시간이 오래 걸렸다.
호흡이 길어 보이는 문제는 정확하게 어떻게 구현할지 써놓고 시작하는 게 도움이 될 듯 하다.
#include <iostream>
#include <queue>
#include <vector>
#include <cstring>
#include <algorithm>
using namespace std;
int n, m;
int map[9][9];
int tmp[9][9];
int totalzero = 0;
int answer = 0;
vector<pair<int, int> > pos;
vector<int> dir;
int dx[4] = {0, -1, 0, 1}; // 우상좌하 반시계
int dy[4] = {1, 0, -1, 0};
void copy(){
for(int i=0; i<n; i++){
for(int j=0; j<m; j++){
tmp[i][j] = map[i][j];
}
}
}
void draw(int x, int y, int dir){
while(1){
int nx = x + dx[dir];
int ny = y + dy[dir];
if(nx < 0 || ny < 0 || nx >= n || ny >= m || tmp[nx][ny] == 6)
break;
if(tmp[nx][ny] == 0){
tmp[nx][ny] = -1;
}
x = nx;
y = ny;
}
}
void activate(int dir, int type, pair<int, int> pos){
int x = pos.first;
int y = pos.second;
if(type == 1){
draw(x, y, dir);
} else if(type == 2){
draw(x, y, dir);
draw(x, y, (dir+2)%4);
} else if(type == 3){
draw(x, y, dir);
draw(x, y, (dir+1)%4);
} else if(type == 4){
draw(x, y, dir);
draw(x, y, (dir+1)%4);
draw(x, y, (dir+2)%4);
} else {
draw(x, y, dir);
draw(x, y, (dir+1)%4);
draw(x, y, (dir+2)%4);
draw(x, y, (dir+3)%4);
}
}
void bfs(){
copy();
for(int i=0; i<pos.size(); i++){
int nowdir = dir[i]; // i번째 CCTV의 방향
int type = map[pos[i].first][pos[i].second]; // CCTV의 성향
activate(nowdir, type, pos[i]);
}
int cnt = 0;
for(int i=0; i<n; i++){
for(int j=0; j<m; j++){
if(tmp[i][j] == 0){
cnt++;
}
}
}
answer = min(answer, cnt);
}
void dfs(int idx, int cnt){
if(cnt == pos.size()){
bfs();
return;
}
for(int j=0; j<4; j++){
dir.push_back(j);
dfs(idx+1, cnt+1);
dir.pop_back();
}
}
int main(){
ios::sync_with_stdio(false);
cin.tie(0);
cout.tie(0);
cin >> n >> m;
for(int i=0; i<n; i++){
for(int j=0; j<m; j++){
cin >> map[i][j];
if(map[i][j] == 0) totalzero++;
else if(map[i][j] > 0 && map[i][j] < 6){ // 1부터 5는 CCTV
pos.push_back(make_pair(i, j));
}
}
}
answer = totalzero;
dfs(0, 0);
cout << answer;
return 0;
}