[ 簡単な説明 ]
チェスのクイーンは、縦、横、斜めの8方向に直進できる駒ですが、これをチェス盤(8×8)の上に8個おけるだろうか? というのが「8クイーン」という問題です。(明らかに8個を超えては置けません。) 「Nクイーン」はこれを拡張し、N×Nの盤の上にN個のクイーンを置こう という問題となります。 N=2、3の場合は解はありません。N=4以上で解が存在します。 N=4以上のケースにおける解の個数は、下記となります。 |
N | 解の個数 | |
全パターン数 | 鏡像解を除く パターン数 |
|
4 | 1 | 1 |
5 | 10 | 2 |
6 | 4 | 1 |
7 | 40 | 6 |
8 | 92 | 12 |
9 | 352 | 46 |
10 | 724 | 92 |
11 | 2680 | 341 |
12 | 14200 | − (*1) |
出力例は、最もポピュラーな8クイーン問題(N=8)を、鏡像解排除オプションにて解いた例です。 nqueen 8 -a -d 鏡像解(回転・反転及びその組合せで同じパターンとなる解)の排除は、解が1つ求まるたびに、他の7つの鏡像解を求めて、リストに登録しておき、解の候補ができた際にそのリストでチェックするというオーソドックスなものです。 8クイーンの解は、全部で92パターンで、鏡像解を除くと12パターンとなります。 |
/* nqueen.c */ /* original : C MAGAZINE 1991年8月号「アルゴリズムとデータ構造入門」 8クイーン求解プログラム。 history : 鏡像解排除ルーチン追加。 */ #include <stdio.h> #include <stdlib.h> #include <string.h> #define SUCCESS 1 /* 成功。 */ #define FAIL 0 /* 失敗。 */ #define FREE 1 /* この場所は利き筋でない。(置ける) */ #define NOT_FREE 0 /* この場所は利き筋。 (置けない) */ typedef struct qlist { char **qpos; struct qlist *next; } QLIST; QLIST *getqlist(int n); void set_solution(int pos[], int n); int check_solution(int pos[], int n); void usage(char *prog); void init_board(int n, int no_mirror); void print_queens(int n); void try_all(int a, int n, int no_mirror); int try(int a, int n); void nqueen(int n, int do_all, int no_mirror); QLIST *start; /* 見つかった解とその鏡像解 */ int *pos; /* 各行に置かれたクイーンの位置。 */ int *col; /* クイーンが垂直方向に利いているかを示す配列。 */ int *down; /* クイーンが右斜め下向きに利いているかを示す配列。 */ int *up; /* クイーンが右斜め上向きに利いているかを示す配列。 */ QLIST *getqlist(int n) { QLIST *p; int i; p = (QLIST *)malloc(sizeof(QLIST)); if(p == NULL) { fprintf(stderr, "Error : out of memory in getqlist()\n"); exit(-1); } p->qpos = (char **)malloc(8 * sizeof(char *)); if(p->qpos == NULL) { fprintf(stderr, "Error : out of memory in getqlist()\n"); exit(-1); } p->qpos[0] = (char *)calloc(8 * n, sizeof(char)); if(p->qpos[0] == NULL) { fprintf(stderr, "Error : out of memory in getqlist()\n"); exit(-1); } for(i = 1; i < 8; i++) p->qpos[i] = p->qpos[i - 1] + n; p->next = NULL; return p; } void set_solution(int pos[], int n) { QLIST *p, *q; int i, j, j4, n1; for(p = start; p->next != NULL; p = p->next) ; n1 = n - 1; for(i = 0; i < n; i++) p->qpos[0][i] = pos[i]; for(i = 0; i < n; i++) p->qpos[1][pos[i]] = n1 - i; for(i = 0; i < n; i++) p->qpos[2][n1 - i] = n1 - pos[i]; for(i = 0; i < n; i++) p->qpos[3][n1 - pos[i]] = i; for(j = 0, j4 = 4; j < 4; j++, j4++) for(i = 0; i < n; i++) p->qpos[j4][i] = n1 - p->qpos[j][i]; q = getqlist(n); p->next = q; } int check_solution(int pos[], int n) { QLIST *p; int i, j; p = start; do { for(i = 1; i < 8; i++) { for(j = 0; j < n; j++) if(pos[j] != p->qpos[i][j]) break; if(j == n) return 0; } p = p->next; } while(p->next != NULL); return 1; } void usage(char *prog) { fprintf(stderr, "使用法 : %s N [ -a [ -d ]]\n", prog); fprintf(stderr, " N : クイーンの数(省略付加)\n"); fprintf(stderr, " -a : すべての解を表示するオプション\n"); fprintf(stderr, " -d : 鏡像解を排除するオプション(-a オプション指定時のみ有効)\n"); fprintf(stderr, " (デフォルトでは最初の解のみ表示)\n"); exit(0); } void init_board(int n, int no_mirror) /* クイーンの位置。利き筋を初期化する。 */ { int i; for (i = 0; i < n; i++) pos[i] = -1; for (i = 0; i < n; i++) col[i] = FREE; for (i = 0; i < 2 * n - 1; i++) down[i] = FREE; for (i = 0; i < 2 * n - 1; i++) up[i] = FREE; if(no_mirror) start = getqlist(n); } void print_queens(int n) /* クイーンの位置を出力する。 */ { static long count = 0; int i, j; printf("*** solution %ld ***\n", ++count); for(i = 0; i < n; i++) { for(j = 0; j < n; j++) printf((pos[i] == j)? "Q ": ". "); printf("\n"); } printf("\n"); } void try_all(int a, int n, int no_mirror) /* a行目以降すべての行にクイーンを置いてみる。 (すべてのパターンを表示する) */ { static int flag = 1; int b; /* 左から右に向かって順番にクイーンが 置けるかどうかを調べる。 */ for(b = 0; b < n; b++) { /* a行目のb番目に置けるかどうか調べる。 */ if(col[b] == FREE && up[a + b] == FREE && down[a - b + (n-1)] == FREE) { /* 置くことができた。 */ pos[a] = b; /* 場所を記録して、利き筋をセットする。 */ col[b] = NOT_FREE; up[a + b] = NOT_FREE; down[a - b + (n-1)] = NOT_FREE; /* N個のクイーンをすべて置くことができれば成功である。 */ if (a + 1 >= n) { if(no_mirror == 0) print_queens(n); else { if(flag || check_solution(pos, n)) { set_solution(pos, n); print_queens(n); } flag = 0; } } else try_all(a + 1, n, no_mirror); pos[a] = -1; /* クイーンを取り除く。 */ col[b] = FREE; up[a + b] = FREE; down[a - b + (n-1)] = FREE; } } } int try(int a, int n) /* a行目以降すべての行にクイーンを置いてみる。 */ { int b; /* 左から右に向かって順番にクイーンが 置けるかどうかを調べる。 */ for(b = 0; b < n; b++) { /* a行目のb番目に置けるかどうか調べる。 */ if(col[b] == FREE && up[a + b] == FREE && down[a - b + (n-1)] == FREE) { /* 置くことができた。*/ pos[a] = b; /* 場所を記録して、利き筋をセットする。 */ col[b] = NOT_FREE; up[a + b] = NOT_FREE; down[a - b + (n-1)] = NOT_FREE; /* N個のクイーンをすべて置くことができれば成功である。 */ if(a + 1 >= n) return SUCCESS; /* a+1行目以降のすべての行に置けたら成功である。 */ if(try(a + 1, n) == SUCCESS) return SUCCESS; /* 失敗した。クイーンを取り除く。 */ pos[a] = -1; col[b] = FREE; up[a + b] = FREE; down[a - b + (n-1)] = FREE; } } return FAIL; /* 結局この行には置ける場所がなかった。 */ } void nqueen(int n, int do_all, int no_mirror) { pos = (int *)calloc(n, sizeof(int)); col = (int *)calloc(n, sizeof(int)); down = (int *)calloc(2 * n - 1, sizeof(int)); up = (int *)calloc(2 * n - 1, sizeof(int)); if(pos == NULL || col == NULL || down == NULL || up == NULL) { fprintf(stderr, "Error : out of memory in nqueen()\n"); exit(-1); } init_board(n, no_mirror); if(do_all) try_all(0, n, no_mirror); else { if(try(0, n) == SUCCESS) print_queens(n); else printf("Sorry, but there is no solution.\n"); } free((char *)pos); free((char *)col); free((char *)down); free((char *)up); } int main(int argc, char *argv[]) { int do_all = 0, no_mirror = 0, n; if(argc < 2) usage(argv[0]); n = atoi(argv[1]); if(n < 4) { fprintf(stderr, "Error : illegal parameter input.\n"); exit(-1); } if(argc >= 3) { if(strcmp(argv[2], "-a") == 0 || strcmp(argv[2], "-A") == 0) do_all = 1; if(argc >= 4 && strcmp(argv[3], "-d") == 0 || strcmp(argv[2], "-D") == 0) no_mirror = 1; } nqueen(n, do_all, no_mirror); return 1; } |
*** solution 1 *** *** solution 2 *** *** solution 3 *** Q . . . . . . . Q . . . . . . . . Q . . . . . . . . . . Q . . . . . . . . Q . . . . . Q . . . . . . . . . . . Q . . . . . . . Q . . . . . Q . . . . . . . Q . . . . Q . . . . . . . . . . . . Q . . Q . . . . . . . . . . . Q . . . Q . . . . . . . . . . . Q . . . . Q . . . . Q . . . . . . . . Q . . . . . . . Q . . . . . . . . . . . . Q . . . . Q . . . . . . . . Q . . . . . . . Q . . . *** solution 4 *** *** solution 5 *** *** solution 6 *** . Q . . . . . . . Q . . . . . . . Q . . . . . . . . . . Q . . . . . . . Q . . . . . . . . Q . . . . . . . . Q . . . . . . . Q . Q . . . . . . . Q . . . . . . . . . . Q . . . . . . . . . . Q . . . Q . . . . . Q . . . . . . . . . . Q . . . . . . . . . . . Q . . . . . . . Q . . . . . . . Q . . . . . Q . . . . . . . Q . . . . Q . . . . . . . . Q . . . . . . Q . . . . . . . . . Q . . . *** solution 7 *** *** solution 8 *** *** solution 9 *** . Q . . . . . . . Q . . . . . . . Q . . . . . . . . . . . Q . . . . . . . . Q . . . . . . . Q . . . . . . . . Q . . Q . . . . . . . . . Q . . . . . Q . . . . . . . . . . Q . . . . . . . . . Q Q . . . . . . . . . . . . . . Q Q . . . . . . . . . . Q . . . . . . . . Q . . . . . . Q . . . . . . . . . . Q . Q . . . . . . . . . . . . Q . . . . . . Q . . . . . . Q . . . . . . Q . . . . . *** solution 10 *** *** solution 11 *** *** solution 12 *** . . Q . . . . . . . Q . . . . . . . Q . . . . . . . . . Q . . . . . . . Q . . . . . . . . Q . . . Q . . . . . . . . . . . . . Q . Q . . . . . . . . . . . . . Q . . . Q . . . . . . . . Q . . . Q . . . . . . . Q . . . . . . . . . . . . . . Q . . . . . . Q . . . . . . . Q . Q . . . . . . . . . . Q . . . . . Q . . . . . . . . . . . . Q . . . . . . Q . . . . . . . Q . . . . . Q . . . . |