BOJ 17492

바둑알 점프

문제출처

문제 해석

  • 바둑알을 1개만 남길 수 있는지 맞추는 문제입니다.
  • 바둑알은 가로,세로,대각선으로 인접한 바둑알 하나를 점프하여 움직일 수 있습니다.
  • 뛰어넘은 바둑알은 제거됩니다.
  • 바둑알은 항상 빈칸으로만 이동할 수 있고, 벽을 뛰어넘을 수 없습니다.

설계(10)

  • 모든 바둑알을 8방향으로 움직여보면서 탐색하려면 일반적으로 O((7x8)^7)의 시간복잡도가 필요하여 시간초과가 날 것입니다.
  • 그러나, 바둑알이 제거되는 경우를 고려하면 그리 크지 않은 시간복잡도로, 단순 완전탐색만으로 풀이가 가능해 보입니다.
  • 기존 정보는 (0:빈칸, 1:벽, 2: 바둑알)이지만 각 바둑알을 독립적으로 생각하기 위해서 인덱스를 붙입니다. 벽은 -1로 변경하고, 1부터 시작하는 인덱스를 바둑알에 붙입니다.
  • 또한, 각 바둑알의 좌표를 저장해놓습니다.
  • 백트래킹을 이용해 이동가능한 바둑알을 이동시키면서 바둑알이 1개만 남을 때까지 탐색합니다.
  • 최적화를 위해서 만약 바둑알이 1개만 남은 경우를 찾으면 즉시 해당 함수를 return 하도록 하였습니다.

구현(30)

코드
#include <iostream>
#include <cstring>
using namespace std;
#define MAXN 10
typedef pair<int,int> P;

int map[MAXN][MAXN];
int N;
P coord[MAXN];
int dx[] = {-1,-1,-1,0,0,1,1,1};
int dy[] = {-1,0,1,-1,1,-1,0,1};
int idx;
bool ans;
bool is_range(int x, int y){
    return (x>=0 && y >= 0 && x < N && y < N);
}
void simulate(int cnt){
    if (ans) return;
    if (cnt == idx-1){
        ans = true;
        return;
    }
    for (int i = 1; i <= idx; i++){
        // 바둑알이 제거된 것은 좌표를 -1로 두어 표시하였습니다.
        if (coord[i].first == -1) continue;
        int x = coord[i].first;
        int y = coord[i].second;
        for (int j = 0; j < 8; j++){
            int nx = x + dx[j];
            int ny = y + dy[j];
            if (!is_range(nx,ny)) continue;
            int nx1 = nx + dx[j];
            int ny1 = ny + dy[j];
            if (map[nx][ny]>0 && is_range(nx1,ny1)&& map[nx1][ny1]==0){
                P tmp_coord = coord[map[nx][ny]];
                coord[map[x][y]] = {nx1,ny1};
                coord[map[nx][ny]] = {-1,-1};
                int next_num = map[nx][ny];
                int cur_num = map[x][y];
                map[nx1][ny1] = map[x][y];
                map[nx][ny] = 0;
                map[x][y] = 0;
                simulate(cnt+1);
                map[nx1][ny1] = 0;
                map[x][y] = cur_num;
                map[nx][ny] = next_num;
                coord[cur_num] = {x,y};
                coord[next_num] = tmp_coord;
            }
        }
    }
}
int main(){
    ios::sync_with_stdio(false);
    cin.tie(0);
    cin >> N;
    for (int i= 0; i<N; i++){
        for (int j = 0;j<N; j++){
            cin >> map[i][j];
            if (map[i][j]==1) map[i][j] = -1;
            else if (map[i][j] ==2) {
                map[i][j] = ++idx;
                coord[idx] = {i,j};
            }
        }
    }
    simulate(0);
    cout << ((ans==true)? "Possible":"Impossible") <<"\n";
    return 0;
}
디버깅
  • 임시저장해놓는 tmp_coord에 뛰어넘을 바둑알의 좌표를 넣지 않고, 이동할 바둑알의 현재좌표를 저장하는 실수를 하였습니다.
제출결과
  • 0ms

마무리

  • 완전탐색문제였습니다. 개인적으로 이게 시간안에 될까 걱정을 했는데 다행이 통과했습니다. 바둑알에 인덱스를 주는 방식을 생각하는게 키포인트였습니다.