2019년 11월 30일 토요일

8퀸 문제 (재귀 호출)




8개의 퀀을 8x8판에 배치시키는데 서로가 잡힐 수 없도록 배치하는 가지수를 찾아내는 것이다.

퀸은 자신과 같은 열에 있는 다른 퀸을 공격하므로 각 열에 퀸을 1개만 배치해야 한다.
pos라는 배열은 각 배열의 인덱스가 열이 되고, 각 인덱스의 값들이 행이 된다.
즉 한 줄로 8x8의 배치 형태를 표시할 수 있다.


문제에 접근하기 앞서 순차적으로 문제에 접근을 해보자.
이것은 단순히 하나의 열에 퀸이 있다면 그 열에는 퀸이 있을 수 없다는 규칙만을 적용하여 도출된 계산이다.
pop[0]에 0이 있다면 퀸은 최상단좌측에 한개가 위치한 것인데 0열은 또 다른 퀸이 들어갈 수 없으므로 그 다음 열에 퀸이 들어갈 경우의 수가 8가지가 된다.
이런식으로 계산을 하면 $8^8$ 가지의 퀸의 배치 경우의 수가 도출된다.
이것은 단순 가지 뻗기 방식의 퀸의 배치방법이다.

* 가지 뻗기 식의 퀸의 배치

public class QueenB {
    static int[] pos = new int[8]; //각 열의 퀸의 위치를 숫자로 저장

    //각 열의 퀸의 위치를 출력합니다.
    static void print() {
        for (int i = 0; i < 8; i++)
            System.out.printf("%2d", pos[i]);
        System.out.println();
    }

    //i열에 퀸을 놓습니다.
    static void set(int i) { //i:열
        for (int j = 0; j < 8; j++) {
            pos[i] = j; //j:행, 퀸을 j행에 배치합니다.
            if (i == 7)
                print(); //배치된 퀸들을 출력한다.
            else
                set(i + 1); //다음 열에 퀸을 배치한다.
        }
    }
    public static void main(String[] args) {
        set(0); //0열에 퀸을 배치한다.
    }
}


여기서 각 열에 퀸을 1개를 배치하는 조건 뿐만이 아니라 각 행에도 퀸을 한개씩만 배치하여야 하는 조건을 추가해 보자.


public class QueenBB {
    static boolean[] flag = new boolean[8]; //각 행에 퀸을 배치했는데 체크하는 배열
    static int[] pos = new int[8]; //각 열의 퀸의 위치

    //각 열의 퀸의 위치를 출력합니다.
    static void print() {
        for (int i = 0; i < 8; i++)
            System.out.printf("%2d", pos[i]);
        System.out.println();
    }

    static void set(int i) {
        for (int j = 0; j < 8; j++) {
            if(flag[j] == false) { //j행에 퀸을 배치 하지 않았다면
                pos[i] = j; //퀸을 j행에 배치한다.
                if(i == 7) 
                    print();
                else{
                    flag[j] = true; //j행에 배치했으니 flag를 true로 변경
                    set(i + 1); //그리고 다음 열에 퀸을 배치할 때 0행에는 배치가 되지 않는다.
                    flag[j] = false; //그리고 다시 j행은 false 로 변경한다
                }
            }
        }
    }
    public static void main(String[] args) {
        set(0); //0열에 퀸을 배치한다.
    }
}


퀸은 가로 세로 뿐만아니라 대각선으로도 이동할 수가 있다.
따라서 퀸이 배치가 되면 배치된 퀸에서 대각선으로도 퀸을 배치하지 않도록 해야한다.




public class EightQueen {
    static boolean[] flag_a = new boolean[8]; //각 행에 퀸을 배치했는지 체크
    static boolean[] flag_b = new boolean[15]; //↗대각선 방향으로 퀸을 배치했는지 체크
    static boolean[] flag_c = new boolean[15]; //↘대각선 방향으로 퀸을 배치했는지 체크
    static int[] pos = new int[8];

    //각 열의 퀸의 위치를 출력합니다.
    static void print() {
        for (int i = 0; i < 8; i++)
            System.out.printf("%2d", pos[i]);
        System.out.println();
    }

    //i열의 알맞은 위치에 퀸을 배치
    static void set(int i) {
        for (int j = 0; j < 8; j++) {
            if (flag_a[j] == false &&
                    flag_b[i + j] == false &&
                    flag_c[i - j + 7] == false) {
                pos[i] = j;
                if (i == 7)
                    print();
                else {
                    flag_a[j] = flag_b[i + j] = flag_c[i - j + 7] = true;
                    set(i + 1);
                    flag_a[j] = flag_b[i + j] = flag_c[i - j + 7] = false;
                }
            }

        }
    }
    public static void main(String[] args) {
        set(0); //0열에 퀸을 배치한다.
    }

}

대각선 방향에 퀸이 배치가 되었는지 체크하는 배열의 길이가 15개인 이유는 8x8 정사각형의 대각선 방향은 2가지 인데 각 방향마다 15개의 사선이 그려진다.
맨 처음 퀸이 위치하면 가로와 대각선방향으로는 퀸이 위치해서는 안된다. ↗대각선 방향의 사선들 중에 하나의 ↗은 ↗에 배치된 퀸의 행(j)과 열(i)의 합이 동일하다.
고로 flag_b[i + j]를 true로 만든 후 경우의 수를 제거해야한다.
이러한 방법으로 ↘대각선 방향도 규칙을 찾아내어 공식으로 만들면 된다.







댓글 없음:

댓글 쓰기