[백트랙킹]백준1799번 비숍

업데이트:

백준1799 비숍

_2021-03-01__8 20 07

N-Queens 문제와 비슷하게 백트랙킹으로 해결할 수 있는 문제였다.

전체 체스판은 오른쪽 대각선과 왼쪽 대각선으로 나눌 수 있다.

비숍은 대각선으로만 움직일 수 있기 때문에 하나의 비숍을 놓았을 때 해당 비숍이 움직일 수 있는 대각선들을 사용하지 못하게 만든다.

최대로 놓는 경우이기 때문에 해당 대각선들을 가능한 많이 막아주면 된다.

체스판과 대각선에 대한 자세한 내용은 N-Queens를 참고하면 된다.

Untitled

체스판에는 다음과 같은 오른쪽 대각선이 존재한다.

하나의 비숍이 정해지면 같은 오른쪽 대각선에 존재하는 칸들에는 다른 비숍이 들어올 수 없다.

두 개의 칸이 같은 오른쪽 대각선에 존재하는 지의 여부는 x+y의 값이 같은지의 여부로 알 수 있다.

예를 들어 (1,2)와 (3,0) 은 x+y의 값이 같기 때문에 같은 오른쪽 대각선 상에 있다.

Untitled 1

체스판에는 다음과 같은 왼쪽 대각선이 존재한다.

하나의 비숍이 정해지면 같은 왼쪽 대각선에 있는 칸들에는 다른 비숍이 들어올 수 없다.

두 개의 칸이 같은 왼쪽 대각선에 속하는 지의 여부는 x-y의 값이 같은 지의 여부로 알 수 있다.

다만 빼기 연산은 값이 마이너스까지 나온다. 위의 4 * 4 체스판을 예로 들면, 아래서부터

-3, -2, -1, 0, 1, 2, 3 이 나오기 때문에 N -1 을 더해줘서 0부터 시작하게 만들어줘야 한다.

그 다음은 백트랙킹의 영역이다.

입력을 받을 때 들어오는 1들을 따로 벡터에 담고, 그 안에서 백트랙킹을 수행하면 된다.

만약 해당 1 칸에 아직 비숍이 놓아지지 않았고 같은 오른쪽, 왼쪽 대각선 상에 모두 비숍이 없다면 비숍을 놓게 만들어준다.

void btk_0(int n){
    if(n==v_0.size()){
        if(bishopCount_0 > answer_0) answer_0 = bishopCount_0;
        return;
    }
    for(int i=n;i<v_0.size();i++){
        if(!isUsed_0[i] && !upRight[v_0[i].first+v_0[i].second] && !upLeft[v_0[i].second - v_0[i].first + N - 1] ){
            isUsed_0[i] = true;
            upRight[v_0[i].first+v_0[i].second] = true;
            upLeft[v_0[i].second - v_0[i].first + N - 1] = true;
            bishopCount_0++;
            btk_0(i+1);
            bishopCount_0--;
            isUsed_0[i] = false;
            upRight[v_0[i].first+v_0[i].second] = false;
            upLeft[v_0[i].second - v_0[i].first + N - 1] = false;
        }
    }
    
}

다만 의아한 것은 종료조건일텐데, 백트랙킹의 종료 조건은 모든 1을 검사한 경우이다.

따라서 백트랙킹 함수로는 현재 내가 검사한 1이 몇 번째 1이었는지를 넘겨주게 되면 된다.

여기까지 수행했다면 정답이긴 하지만 시간초과가 발생한다.

10초라서 매우 넉넉하다고 생각했는데 아니었다.

여기저기 찾아본 결과 시간 초과가 발생하지 않게 하려면 처음부터 입력으로 들어오는 1들을 두 가지 종류로 나눠야 한다.

체스판 위에서 하얀색 칸 위에 있는 비숍은 하얀색 칸으로만 갈 수 있고, 검정색 칸 위에 있는 비숍은 검정색 칸 위로만 갈 수 있다.

검정색 칸인지 하얀색 칸인지는 해당 칸의 x, y 좌표를 더한 값을 2로 나눈 나머지를 이용하면 된다.

따라서 1을 두 개로 분류한 뒤 각각의 그룹에 대해 백트랙킹을 수행하고 마지막에 결과를 더해주면 된다.

이 과정을 생략할 경우 시간 초과가 나는 이유는 하얀색 비숍인 경우에는 검정색 비숍을 무시해도 되는데도 불구하고 모두 확인하기 때문이다.

#include <iostream>
#include <vector>

using namespace std;
bool upRight[25];
bool upLeft[25];

int N;
int answer = 0;
int answer_0 = 0;
int answer_1 = 0;
int bishopCount = 0;
int bishopCount_0 = 0;
int bishopCount_1 = 0;
int board[11][11];
vector<pair<int,int> > v;
vector<pair<int,int> > v_0;
vector<pair<int,int> > v_1;
vector<bool> isUsed;
vector<bool> isUsed_0;
vector<bool> isUsed_1;

void btk_0(int n){
    if(n==v_0.size()){
        if(bishopCount_0 > answer_0) answer_0 = bishopCount_0;
        return;
    }
    for(int i=n;i<v_0.size();i++){
        if(!isUsed_0[i] && !upRight[v_0[i].first+v_0[i].second] && !upLeft[v_0[i].second - v_0[i].first + N - 1] ){
            isUsed_0[i] = true;
            upRight[v_0[i].first+v_0[i].second] = true;
            upLeft[v_0[i].second - v_0[i].first + N - 1] = true;
            bishopCount_0++;
            btk_0(i+1);
            bishopCount_0--;
            isUsed_0[i] = false;
            upRight[v_0[i].first+v_0[i].second] = false;
            upLeft[v_0[i].second - v_0[i].first + N - 1] = false;
        }
    }
    
}
void btk_1(int n){
    if(n==v_1.size()){
        if(bishopCount_1 > answer_1) answer_1 = bishopCount_1;
        return;
    }
    for(int i=n;i<v_1.size();i++){
        if(!isUsed_1[i] && !upRight[v_1[i].first+v_1[i].second] && !upLeft[v_1[i].second - v_1[i].first + N - 1] ){
            isUsed_1[i] = true;
            upRight[v_1[i].first+v_1[i].second] = true;
            upLeft[v_1[i].second - v_1[i].first + N - 1] = true;
            bishopCount_1++;
            btk_1(i+1);
            bishopCount_1--;
            isUsed_1[i] = false;
            upRight[v_1[i].first+v_1[i].second] = false;
            upLeft[v_1[i].second - v_1[i].first + N - 1] = false;
        }
    }
    
}

int main(){
    ios_base::sync_with_stdio(0);
    cin.tie(0);
    cin >> N;
    for(int i=0;i<N;i++){
        for(int j=0;j<N;j++){
            cin >> board[i][j];
            
            if(board[i][j]==1){
                if((i+j)%2 == 0){
                    v_0.push_back(make_pair(i, j));
                    isUsed_0.push_back(false);
                }
                else{
                    v_1.push_back(make_pair(i, j));
                    isUsed_1.push_back(false);
                }
//                v.push_back(make_pair(i, j));
//                isUsed.push_back(false);
            }
        }
    }
//    btk(0,(v[0].first+v[0].second));
    btk_0(0);
    btk_1(0);
    cout << answer_0 + answer_1;
}

카테고리:

업데이트:

댓글남기기