BOJ 17500

국경

문제출처

문제 해석

  • NxN모양의 격자에서 국경을 만들 수 있는지 판단하는 문제입니다.
  • 다음의 조건들을 만족해야 합니다.
    1. 국경은 왼쪽 상단 끝에서 시작하여 오른쪽 하단 끝으로 가는 하나의 경로여야 합니다.
    2. 국경의 경로는 칸의 내부로 진입할 수 없고, 오로지 칸의 선분을 지나는 형태로 만들어야 합니다.
    3. 경로는 같은지점을 반복해서 지날 수 없습니다.
    4. 서로 다른 종이 국경을 지나지 않고 만날 수 있으면 안됩니다.
    5. 같은 종끼리 만나지 못해도 상관없습니다.
    6. 나라 안에 아무 종도 없어도 됩니다.

설계(20)

  • 백트래킹과 완전탐색으로 구현이 가능한 문제입니다.
  • 먼저 바다와 길목(+), 주어진 입력을 매핑합니다.
  • (1,1)부터 시작하여 (2xN+1,4xN+1)까지 가는 경로를 백트래킹으로 탐색합니다.
  • 좌우로는 4칸씩 이동해야 하고, 상하로는 2칸씩 이동해야 합니다.
  • 경로를 방문했는지 여부를 판단할 때, 처음에는 다음 +지점을 방문하지 않았을 때, +까지 지나가는 경로를 조건에 맞게 ‘-‘또는 ‘l’을 채워주도록 하였습니다.
  • 그러나, 알수없는 계속된 WA로 인해 다음칸이 빈공간일 경우 +가 나올때까지 ‘-‘또는 ‘l’로 채워주는 방식으로 변경하였습니다.
  • 경로가 오른쪽 하단 꼭지점까지 완성되면 각 동물들에 대해 bfs탐색을 하여 다른종이 국경을 지나지 않고 만날 수 있는 지 확인합니다.
  • 만약 위의 경우에 해당하지 않으면(모든 조건 만족) 출력을 한 후, 출력을 여러번 하는 것을 방지하기 위해 flag변수를 체크해주고 backtracking함수를 빠져나오게 합니다.

구현(50)

코드
#include <iostream>
#include <cstring>
#include <queue>
using namespace std;
#define MAXN 20

char map[MAXN][MAXN];
bool visited[MAXN][MAXN];
bool check[MAXN][MAXN];

int dx[] = {0,0,1,-1};
int dy[] = {1,-1,0,0};
int N;
bool flag = false;
bool possible;
bool is_range(int x, int y){
    return (x>= 2 && y >= 3 && x < 2*N+1 && y< 4*N+1);
}
void bfs(int x, int y, char target){
    queue<pair<int,int> > q;
    q.push({x,y});
    check[x][y] = true;
    while (!q.empty()){
        int x = q.front().first;
        int y = q.front().second;
        q.pop();
        for (int k = 0; k< 4; k++){
            int nx = x + dx[k];
            int ny = y + 2*dy[k];
            int nx1 = x + 2*dx[k];
            int ny1 = y + 4*dy[k];
            if (map[nx][ny] == '-' || map[nx][ny] == '|') continue;
            if (!is_range(nx1,ny1) || check[nx1][ny1]) continue;
            if (map[nx1][ny1] != '.' && map[nx1][ny1] != target){
                possible = true;
                return;
            }
            else{
                check[nx1][ny1] = true;
                q.push({nx1,ny1});
            }
        }
    }
}
void backtrack(int x, int y){
    if (flag) return;
    if (x == 2*N+1 && y == 4*N+1){
        possible = false;
        memset(check,false,sizeof(check));

        for (int i = 2; i < 2*N+2 && !possible; i+=2){
            for (int j=3 ; j< 4*N+2; j+=4){
                if (!check[i][j] && map[i][j] != '.'){
                    bfs(i,j,map[i][j]);
                }
            }
        }
        if (!possible){
            cout <<"yes\n";
            for (int i =0 ; i < 2*N+3; i++){
                for (int j=0 ;j<4*N+3; j++){
                    cout << map[i][j];
                }
                cout <<"\n";
            }
            flag = true;
        }
        return;
    }
    if (visited[x][y]) return;
    for (int k = 0; k< 4; k++){
        int nx = x + dx[k];
        int ny = y + dy[k];
        if (map[nx][ny] == '#' || map[nx][ny] != ' ') continue;
        visited[x][y] = true;
        while (map[nx][ny] != '+'){
            if (k < 2) map[nx][ny] = '-';
            else map[nx][ny] = '|';
            nx += dx[k];
            ny += dy[k];
        }
        backtrack(nx,ny);
        nx -= dx[k];
        ny -= dy[k];
        while (map[nx][ny]!='+'){
            map[nx][ny] = ' ';
            nx -= dx[k];
            ny -= dy[k];
        }
        visited[x][y] = false;
    }
}
int main(){
    ios::sync_with_stdio(false);
    cin.tie(0);
    cin >> N;
    for (int i = 0; i <2*N+3; i++){
        for (int j = 0 ;j < 4*N+3; j++){
            if (i == 0 || i == 2*N+2 || j == 0 || j == 4*N+2) map[i][j] = '#';
            else map[i][j] =' ';
        }
    }
    for (int i = 1; i < 2*N+2; i+=2){
        for (int j = 1; j < 4*N+2; j+=4){
            map[i][j] = '+';
        }
    }
    for (int i = 2; i < 2*N+2; i+=2){
        for (int j=3; j<4*N+2; j+=4){
            cin >> map[i][j];
        }
    }
    backtrack(1,1);
    if (!flag) cout << "no\n";
    return 0;
}

디버깅
  • 앞서 말했듯이 백트래킹의 방문체크를 하는 부분이 무언가 잘못되어 WA가 계속 나왔는데, 왜 틀렸는지 파악을 하기가 어려웠습니다. 이때문에 다른 방식으로 변경하였고 시간이 너무나 많이 소모가 되었습니다.
제출결과
  • 12ms

마무리

  • 범위가 작아 완전탐색으로도 구현이 가능한 문제였습니다. 그러나, 범위와 방문체크를 하기 까다로웠고 구현을 하기 전에 미리 정확한 인덱스 계산을 했어야 했던 문제입니다. 구현 연습하기에 아주 좋은 문제인 것 같습니다.