Comments
Description
Transcript
[PDF]Nクイーン問題
Nクイー ン問題 Nクイーン問題 0.目次 1.Nクイーン問題 1.1 1.2 1.3 バックトラックに基づく解法 バックトラックに基づく解法の改良 順列生成に基づく解法 - 1 - Nクイー ン問題 1.Nクイーン問題 N 個 の ク イ ー ン を N × N の チ ェ ス 盤 上 に 、 互 い に き き 筋 (縦 、 横 、 斜 め 方 向 )に 入らないように置く方法を考察する。 ●クイーンのきき筋 Q ● Nク イ ー ン 問 題 の 解 の 数 。 N 3 4 5 6 7 8 9 10 解の数 0 2 10 4 40 92 352 724 N 11 12 13 14 15 16 17 18 解の数 2680 14200 73712 365596 2279184 14772512 95815104 666090624 - 2 - N 19 20 21 22 23 24 解の数 4968057848 39029188884 314666222712 2691008701644 24233937684440 227514171973736 Nクイー ン問題 1.1 バックトラックに基づく解法 チ ェ ス 盤 の マ ス 目 を 指 定 す る た め に 、 n× n の 行 列 と み な し 、 i行 目 、 j列 目 の マ ス 目 を (i,j) で 指 定 す る 。 → j列 目 ↓ i行 目 ●バックトラックの考え方 (1)左の列から右の列へ順にひとつずつクイーンを置いていく。 (2)これからクイーンを置こうとする列のすべてのマス目のうち、 ・すでに置かれたクイーンのきき筋に入らないマス目があれば、 そのマス目にクイーンを置き、右隣の列にクイーンを置くことを 続ける。 ・ そ の よ う な マ ス 目 が 複 数 個 あ る と き は そ の 中 で 一 番 下 (行 番 号 の 大 き い 方 を 下 と す る )の マ ス 目 を 選 ぶ 。 ・すべてのマス目がすでに置かれたクイーンのきき筋に入っている ときは、前の列に戻り、クイーンの置かれたマス目のひとつ上から 一番上のマス目までに対して(2)と同様のことを行う。 (3)こうして、最後の列にクイーンが置ければ解になる。 もし、前の列に戻れなくなったら終了となる。 - 3 - Nクイー ン問題 1 2 3 4 状 態 (1) 1 2 3 4 □□□□ □□□□ □□□□ ●□□□ 1 2 3 4 状 態 (6) 1 2 3 4 □●□× □□□× □□●× ●□□× 1 2 3 4 状 態 (11)解 1 2 3 4 □●□□ □□□● ●□□× □□●× 1 2 3 4 状 態 (16) 1 2 3 4 □□●□ ●□×□ □□×□ □●×□ 1 2 3 4 状 態 (21) 1 2 3 4 ●□□□ □□□□ □□□□ □●□□ 1 2 3 4 状 態 (26) 1 2 3 4 ●□×□ □□×□ □●×□ □□×□ 1 2 3 4 状 態 (2) 1 2 3 4 □□□□ □●□□ □×□□ ●×□□ 1 2 3 4 状 態 (7) 1 2 3 4 □●×□ □□×□ □□□□ ●□□□ 1 2 3 4 状 態 (12) 1 2 3 4 □●□× □□□□ ●□□□ □□●□ 1 2 3 4 状 態 (17)解 1 2 3 4 □□●□ ●□□□ □□□● □●□× 1 2 3 4 状 態 (22) 1 2 3 4 ●□□□ □□●□ □□×□ □●×□ 1 2 3 4 状 態 (27) 1 2 3 4 ●×□□ □×□□ □□□□ □□□□ 1 2 3 4 状 態 (3) 1 2 3 4 □□×□ □●×□ □□×□ ●□×□ 1 2 3 4 状 態 (8) 1 2 3 4 □□□□ □□□□ ●□□□ □□□□ 1 2 3 4 状 態 (13) 1 2 3 4 □●×□ □□×□ ●□×□ □□□□ 1 2 3 4 状 態 (18) 1 2 3 4 □□●× ●□□× □□□□ □●□□ 1 2 3 4 状 態 (23) 1 2 3 4 ●□□× □□●× □□□× □●□× 1 2 3 4 状 態 (28)終 了 1 2 3 4 □□□□ □□□□ □□□□ □□□□ 解 の 候 補 は 、 n**n個 に な る 。 - 4 - 1 2 3 4 状 態 (4) 1 2 3 4 □●□□ □□□□ □□□□ ●□□□ 1 2 3 4 状 態 (9) 1 2 3 4 □●□□ □×□□ ●×□□ □×□□ 1 2 3 4 状 態 (14) 1 2 3 4 □□□□ ●□□□ □□□□ □□□□ 1 2 3 4 状 態 (19) 1 2 3 4 □×□□ ●×□□ □×□□ □□□□ 1 2 3 4 状 態 (24) 1 2 3 4 ●□×□ □□□□ □□□□ □●□□ 1 2 3 4 状 態 (5) 1 2 3 4 □●□□ □□□□ □□●□ ●□×□ 1 2 3 4 状 態 (10) 1 2 3 4 □●□□ □□□□ ●□□□ □□●□ 1 2 3 4 状 態 (15) 1 2 3 4 □□□□ ●□□□ □□□□ □●□□ 1 2 3 4 状 態 (20) 1 2 3 4 ●□□□ □□□□ □□□□ □□□□ 1 2 3 4 状 態 (25) 1 2 3 4 ●□□□ □□□□ □●□□ □□□□ Nクイー ン問題 ●4×4のチェス盤における探索過程 (1,4) (2,4) (2,3) (2,2) (2,1) (1,3) (2,4) (2,3) (2,2) (2,1) (3,4) (3,3) (3,2) (3,1) (3,4) (3,3) (3,4) (4,4) (4,3) (4,2) (4,1) (4,4) (4,3) (4,2) (4,1) 解 (3,3) (3,2) (3,1) (1,2) (2,4) (3,4) (3,3) (3,2) (3,1) (4,4) (4,3) (4,2) (4,1) (2,3) (2,2) (2,1) (1,1) (2,4) (3,4) (3,3) (3,2) (3,1) (2,3) (3,4) (3,3) (3,2) (3,1) (2,2) (2,1) - 5 - (4,4) (4,3) (4,2) (4,1) 解 Nクイー ン問題 ●バックトラックに基づくNクイーン問題プログラム (i,j)に ク イ ー ン Qを 置 く 場 合 、 チ ェ ッ ク す る マ ス 目 を 青 で 示 す 。 → j ↓ i Q ● N ク イ ー ン 問 題 プ ロ グ ラ ム (nq111.c) 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 /* << nq111.c >> */ /* N ク イ ー ン 問 題 */ #include <stdio.h> #define N 30 /* チ ェ ス 盤 サ イ ズ の 最 大 値 。 */ int b[N+1][N+1], /* チ ェ ス 盤 を 表 す 配 列 。 b[i][j]=0: マ ス 目 (i,j)に ク イ ー ン が あ る 場 合 。 b[i][j]=1: マ ス 目 (i,j)に ク イ ー ン が な い 場 合 。 */ count, /* 配 置 の 個 数 。 */ n; /* チ ェ ス 盤 の サ イ ズ 。 */ void queen(int i, int j); int main() { int i,j; while( 1 ) { /* nの 読 み 込 み 。 */ scanf("%d",&n); if( (n <= 0)||(n > N) ) { break; } /* チ ェ ス 盤 等 の 初 期 設 定 。 */ for( i=1; i<=n; i++ ) { for( j=1; j<=n; j++ ) { b[i][j] = 0; } } count = 0; /* バ ッ ク ト ラ ッ ク 。 */ queen(n,0); printf("%d× %dの チ ェ ス 盤 解 の 数 : %d\n",n,n,count); } } /* マ ス 目 (i,j)に ク イ ー ン を 置 く 操 作 。 */ void queen(int i, int j) { int p,q; - 6 - Nクイー ン問題 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 /* 横 方 向 の マ ス 目 を 調 べ る 。 */ for( q=j-1; q>=1; q-- ) { /* す で に あ る 場 合 、 戻 る 。 */ if( b[i][q] == 1 ) { return; } } /* 斜 め 上 方 向 の マ ス 目 を 調 べ る 。 */ p = i-1; q = j-1; while( (p >= 1)&&(q >= 1) ) { /* す で に あ る 場 合 、 戻 る 。 */ if( b[p][q] == 1 ) { return; } p--; q--; } /* 斜 め 下 方 向 の マ ス 目 を 調 べ る 。 */ p = i+1; q = j-1; while( (p <= n)&&(q >= 1) ) { /* す で に あ る 場 合 、 戻 る 。 */ if( b[p][q] == 1 ) { return; } p++; q--; } /* き き 筋 に 他 の ク イ ー ン が な い の で 、 j列 目 の マ ス 目 (i,j)に ク イ ー ン を 置 く 。 */ b[i][j] = 1; if( j == n ) { /* 解 の 表 示 。 */ count++; printf("[%d] \n",count); for( p=1; p<=n; p++ ) { for( q=1; q<=n; q++ ) { printf("%3d",b[p][q]); } printf("\n"); } } else { /* 右 隣 の 列 j+1の n個 の マ ス 目 を 調 べ る 。 */ for( p=n; p>=1; p-- ) { queen(p,j+1); } } /* j列 目 の マ ス 目 (i,j)か ら ク イ ー ン を 取 り 除 く 。 */ b[i][j] = 0; } - 7 - Nクイー ン問題 実行結果 % cc nq111.c % a.out 4 [1] 0 1 0 0 0 0 0 1 1 0 0 0 0 0 1 0 [2] 0 0 1 0 1 0 0 0 0 0 0 1 0 1 0 0 4× 4の チ ェ ス 盤 解 の 数 : 2 8 <<途中省略>> 8× 8の チ ェ ス 盤 解 の 数 : 92 0 実行時間 実行時間測定 % time ./a.out 13 0 13× 13の チ ェ ス 盤 解 の 数 : 73712 2.579u 0.001s 0:04.73 54.3% 0+0k 0+8io 0pf+0w % time ./a.out 14 0 14× 14の チ ェ ス 盤 解 の 数 : 365596 16.881u 0.000s 0:18.67 90.4% 0+0k 0+8io 0pf+0w % time ./a.out 15 0 15× 15の チ ェ ス 盤 解 の 数 : 2279184 116.365u 0.000s 1:58.95 97.8% 0+0k 0+8io 0pf+0w - 8 - Nクイー ン問題 1.2 バックトラックに基づく解法の改良 横方向、斜め方向を配列で表現し、すでにクイーンが置かれているかいないか の判断を早める。 横方向 左斜め上方向 (i-j+nが 一 定 ) →j ↓1 i 2 3 4 1 2 3 4 1 4 5 6 7 2 3 4 5 6 3 2 3 4 5 4 1 2 3 4 左斜め下方向 (i+jが 一 定 ) 1 2 3 4 1 2 3 4 5 2 3 4 5 6 3 4 5 6 7 4 5 6 7 8 ●バックトラックに基づくNクイーン問題プログラムの改良 (i,j)に ク イ ー ン を 置 く 場 合 、 チ ェ ッ ク す る マ ス 目 。 → j列 目 ↓ i行 目 Q 青線 ue[i-j+n] 赤線 gyo[i] 緑線 sita[i+j] ● N ク イ ー ン 問 題 プ ロ グ ラ ム (nq121.c) 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 /* << nq121.c >> */ /* N ク イ ー ン 問 題 */ #include <stdio.h> #define N 30 /* チ ェ ス 盤 サ イ ズ の 最 大 値 。 */ int gyo[N+1], /* gyo[i]=0: 行 iに ク イ ー ン が な い 。 gyo[i]=1: 行 iに ク イ ー ン が あ る 。 */ ue[2*N], /* ue[i-j+n]=0:左 斜 め 上 方 向 i-j+nに ク イ ー ン が な い 。 ue[i-j+n]=1:左 斜 め 上 方 向 i-j+nに ク イ ー ン が あ る 。 */ sita[2*N+1], /* sita[i+j]=0: 左 斜 め 下 方 向 i+jに ク イ ー ン が な い 。 sita[i+j]=1: 左 斜 め 下 方 向 i+jに ク イ ー ン が あ る 。 */ kai[N+1], /* 解 : 列 jの 行 kai[j]に ク イ ー ン が あ る 。 */ count, /* 得 ら れ た 配 置 の 個 数 。 */ n; /* チ ェ ス 盤 の サ イ ズ 。 */ void queen(int i, int j); int main() { int k; while( 1 ) { /* nの 読 み 込 み 。 */ - 9 - Nクイー ン問題 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 scanf("%d",&n); if( (n <= 0)||(n > N) ) { break; } /* チ ェ ス 盤 等 の 初 期 設 定 。 */ for( k=1; k<=n; k++ ) { gyo[k] = 0; } for( k=1; k<=2*n-1; k++ ) { ue[k] = 0; } for( k=2; k<=2*n; k++ ) { sita[k] = 0; } count = 0; /* バ ッ ク ト ラ ッ ク 。 */ for( k=n; k>=1; k-- ) { queen(k,1); } printf("%d× %dの チ ェ ス 盤 解 の 数 : %d\n",n,n,count); } } /* マ ス 目 (i,j)に ク イ ー ン を 置 く 操 作 。 */ void queen(int i, int j) { int k; /* 横 方 向 の マ ス 目 を 調 べ 、 す で に あ る 場 合 、 戻 る 。 */ if( gyo[i] == 1 ) { return; } /* 左 斜 め 上 方 向 の マ ス 目 を 調 べ 、 す で に あ る 場 合 、 戻 る 。 */ if( ue[i-j+n] == 1 ) { return; } /* 左 斜 め 下 方 向 の マ ス 目 を 調 べ 、 す で に あ る 場 合 、 戻 る 。 */ if( sita[i+j] == 1 ) { return; } /* き き 筋 に 他 の ク イ ー ン が な い の で 、 マ ス 目 (i,j)に ク イ ー ン を 置 く 。 */ kai[j] = i; gyo[i] = 1; ue[i-j+n] = 1; sita[i+j] = 1; if( j == n ) { /* 解 の 表 示 。 */ count++; printf("[%d]:",count); for( k=1; k<=n; k++ ) { printf("%3d",kai[k]); } printf("\n"); } else { /* 右 隣 の 列 j+1の n個 の マ ス 目 を 調 べ る 。 */ for( k=n; k>=1; k-- ) { queen(k,j+1); } } /* マ ス 目 (i,j)の ク イ ー ン を 取 り 除 く 。 */ kai[j] = 0; - 10 - Nクイー ン問題 71 72 73 74 gyo[i] = 0; ue[i-j+n] = 0; sita[i+j] = 0; } 実行結果 % cc nq121.c % ./a.out 5 [1]: 5 3 1 4 2 [2]: 5 2 4 1 3 <<途中省略>> [10]: 1 3 5 2 4 5× 5の チ ェ ス 盤 解 の 数 : 10 11 <<途中省略>> 11× 11の チ ェ ス 盤 解 の 数 : 2680 0 実行時間 実行時間測定 % time ./a.out 14 0 14× 14の チ ェ ス 盤 解 の 数 : 365596 4.077u 0.000s 0:06.58 61.8% 0+0k 0+0io 0pf+0w % time ./a.out 15 0 15× 15の チ ェ ス 盤 解 の 数 : 2279184 26.523u 0.000s 0:28.89 91.7% 0+0k 0+8io 0pf+0w % time ./a.out 16 0 16× 16の チ ェ ス 盤 解 の 数 : 14772512 183.400u 0.000s 3:08.56 97.2% 0+0k 0+8io 0pf+0w - 11 - Nクイー ン問題 1.3 部分順列生成に基づく解法 解 の 候 補 を n**nか ら n!に 縮 小 す る 。 斜め方向を配列で表現し、効率をあげる。 ●考え方:部分順列の生成 集 合 {x(1),...,x(k)}上 の す べ て の 順 列 を [x(1),...,x(k)]で 表 す 。 k個 の 要 素 か ら 1個 ず つ 抽 出 し て い く 。 [x(1),x(2),...,x(k)] = x(1)[x(2),x(3),...,x(k)] ∪ x(2)[x(1),x(3),...,x(k)] ∪ x(3)[x(2),x(1),...,x(k)] ・・・・・・・・・・・・・ ∪ x(k-1)[x(2),x(3),...,x(1),x(k)] ∪ x(k)[x(2),x(3),...,x(k-1),x(1)] 抽出する順序に任意性があるので種々の生成法が考えられる。 - 12 - Nクイー ン問題 ●考え方1 抽出を繰り返し行う。未決定の先頭要素を残りの要素の先頭から 順に交換していく。 ・樹形図1 [1234] 1[234] 2[134] 3[214] 4[231] 12[34] 123[4] 124[3] 13[24] 132[4] 134[2] 14[32] 143[2] 142[3] 21[34] 213[4] 214[3] 23[14] 231[4] 234[1] 24[31] 243[1] 241[3] 32[14] 321[4] 324[1] 31[24] 312[4] 314[2] 34[12] 341[2] 342[1] 42[31] 423[1] 421[3] 43[21] 432[1] 431[2] 41[32] 413[2] 412[3] - 13 - Nクイー ン問題 ・樹形図2:要素の交換・移動状況 要素を交換した後、すぐ元に戻すことを繰り返す。 こ の よ う に す る と 、順 列 に 関 す る プ ロ グ ラ ム を 記 述 し や す く な る 。 赤い×は要素の交換、青い×は元に戻すことを意味する。 [1234] 1[234] 12[34] 123[4] 1234 124[3] 1243 123[4] 13[24] 132[4] 1324 134[2] 143[2] 1342 1432 142[3] 1423 21[34] 213[4] 2134 23[14] 214[3] 231[4] 234[1] 2143 2314 2341 243[1] 241[3] 2431 2413 321[4] 3214 324[1] 312[4] 314[2] 341[2] 342[1] 3241 3124 3142 3412 3421 423[1] 4231 421[3] 432[1] 431[2] 413[2] 412[3] 4213 4321 4312 4132 4123 12[34] 14[32] 12[34] 2[134] 1[234] 21[34] 24[31] 21[34] 3[214] 32[14] 1[234] 31[24] 34[12] 4[231] 42[31] 1[234] 43[21] 41[32] - 14 - Nクイー ン問題 ● 部 分 順 列 生 成 プ ロ グ ラ ム (nq131.c) 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 /* << nq131.c >> */ /* 集 合 {1,2,… ,n}か ら r個 取 り 出 し ,1 列 に 並 べ る 部 分 順 列 を 生 成 す る 。 */ #include <stdio.h> #define N 30 /* 集 合 の 最 大 要 素 数 。 */ int p[N+1], /* 部 分 順 列 を 保 存 す る 配 列 。 */ n, /* 集 合 の 要 素 数 。 */ r; /* 取 り 出 す 個 数 。 */ double count; /* 部 分 順 列 の 個 数 。 */ void perm(int k); int main() { int i; while( 1 ) { /* n,rの 読 み 込 み 。 */ scanf("%d%d",&n,&r); if( (n <= 0)||(n > N)||(r > n) ) { break; } /* 初 期 設 定 。 */ for( i=1; i<=n; i++ ) { p[i] = i; } count = 0; /* 部 分 順 列 の 生 成 。 */ perm(1); printf("n=%d r=%d count=%16.0f\n",n,r,count); } } /* 部 分 順 列 の 生 成 。 */ void perm(int k) { int i,w; if( k > r ) { /* 部 分 順 列 の 表 示 。 */ count++; printf("%6.0f: ",count); for( i=1; i<=r; i++ ) { printf("%2d ",p[i]); } printf("\n"); return; } for( i=k; i<=n; i++ ) { /* 交 換 。 */ w = p[k]; p[k] = p[i]; p[i] = w; perm(k+1); /* 交 換 を 一 度 戻 す 。 */ w = p[k]; p[k] = p[i]; p[i] = w; } } - 15 - Nクイー ン問題 実行結果 % cc nq131.c % ./a.out 3 1 1: 1 2: 2 3: 3 n=3 r=1 count= 3 2 1: 1 2 2: 1 3 3: 2 1 4: 2 3 5: 3 2 6: 3 1 n=3 r=2 count= 3 3 1: 1 2 2: 1 3 3: 2 1 4: 2 3 5: 3 2 6: 3 1 n=3 r=3 count= 0 0 3 6 3 2 3 1 1 2 6 実行時間 実行時間測定 % time ./a.out 11 11 0 0 n=11 r=11 count= 39916800 1.202u 0.001s 0:05.08 23.6% 0+0k 0+8io 0pf+0w % time ./a.out 12 12 0 0 n=12 r=12 count= 479001600 14.415u 0.000s 0:17.93 80.3% 0+0k 0+8io 0pf+0w % time ./a.out 13 13 0 0 n=13 r=13 count= 6227020800 187.563u 0.011s 3:10.93 98.2% 0+0k 0+8io 0pf+0w - 16 - Nクイー ン問題 データの移動に着目すると、効率のよいプログラムが得られる。 ・樹形図3 [1234] 1[234] 12[34] 123[4] 124[3] 13[24] 132[4] 134[2] 14[32] 143[2] 142[3] 2[134] 3[214] 4[231] 21[34] 213[4] 214[3] 23[14] 231[4] 234[1] 24[31] 243[1] 241[3] 32[14] 321[4] 324[1] 31[24] 312[4] 314[2] 34[12] 341[2] 342[1] 42[31] 423[1] 421[3] 43[21] 432[1] 431[2] 41[32] 413[2] 412[3] - 17 - Nクイー ン問題 ● 部 分 順 列 生 成 プ ロ グ ラ ム (nq132.c) 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 /* << nq132.c >> */ /* 集 合 {1,2,… ,n}か ら r個 取 り 出 し ,1 列 に 並 べ る 部 分 順 列 を 生 成 す る 。 */ #include <stdio.h> #define N 30 /* 集 合 の 最 大 要 素 数 。 */ int p[N+1], /* 部 分 順 列 を 保 存 す る 配 列 。 */ n, /* 集 合 の 要 素 数 。 */ r; /* 取 り 出 す 個 数 。 */ double count; /* 部 分 順 列 の 個 数 。 */ void perm(int k); int main() { int i; while( 1 ) { /* n,rの 読 み 込 み 。 */ scanf("%d%d",&n,&r); if( (n <= 0)||(n > N)||(r > n) ) { break; } /* 初 期 設 定 。 */ for( i=1; i<=n; i++ ) { p[i] = i; } count = 0; /* 部 分 順 列 の 生 成 。 */ perm(1); printf("n=%d r=%d count=%16.0f\n",n,r,count); } } /* 部 分 順 列 の 生 成 。 */ void perm(int k) { int i,w; if( k > r ) { /* 部 分 順 列 の 表 示 。 */ count++; printf("%6.0f: ",count); for( i=1; i<=r; i++ ) { printf("%2d ",p[i]); } printf("\n"); return; } w = p[k]; for( i=k; i<=n; i++ ) { p[k] = p[i]; p[i] = w; perm(k+1); p[i] = p[k]; } p[k] = w; } - 18 - Nクイー ン問題 実行結果 % cc nq132.c % ./a.out 3 1 1: 1 2: 2 3: 3 n=3 r=1 count= 3 2 1: 1 2 2: 1 3 3: 2 1 4: 2 3 5: 3 2 6: 3 1 n=3 r=2 count= 3 3 1: 1 2 2: 1 3 3: 2 1 4: 2 3 5: 3 2 6: 3 1 n=3 r=3 count= 0 0 3 6 3 2 3 1 1 2 6 実行時間 実行時間測定 % time ./a.out 11 11 0 0 n=11 r=11 count= 39916800 1.018u 0.000s 0:04.65 21.7% 0+0k 0+8io 0pf+0w % time ./a.out 12 12 0 0 n=12 r=12 count= 479001600 12.219u 0.000s 0:15.54 78.5% 0+0k 0+8io 0pf+0w % time ./a.out 13 13 0 0 n=13 r=13 count= 6227020800 158.758u 0.000s 2:42.25 97.8% 0+0k 0+8io 0pf+0w - 19 - Nクイー ン問題 ● N ク イ ー ン 問 題 プ ロ グ ラ ム (nq133.c) 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 /* << nq133.c >> */ /* N ク イ ー ン 問 題 */ #include <stdio.h> #define N 30 /* チ ェ ス 盤 サ イ ズ の 最 大 値 。 */ int p[N+1], /* 部 分 順 列 を 保 存 す る 配 列 。 */ count, /* 生 成 さ れ る 解 の 個 数 。 */ n, /* 要 素 数 。 */ ue[2*N], /* ue[i-j+n]=0:左 斜 め 上 方 向 i-j+nに ク イ ー ン が な い 。 ue[i-j+n]=1:左 斜 め 上 方 向 i-j+nに ク イ ー ン が あ る 。 */ sita[2*N+1]; /* sita[i+j]=0:左 斜 め 下 方 向 i+jに ク イ ー ン が な い 。 sita[i+j]=1:左 斜 め 下 方 向 i+jに ク イ ー ン が あ る 。 */ void perm(int k); int main() { int i; while( 1 ) { /* nの 読 み 込 み 。 */ scanf("%d",&n); if( (n <= 0)||(n > N) ) { break; } /* 部 分 順 列 生 成 の 初 期 設 定 。 */ for( i=1; i<=n; i++ ) { p[i] = i; } /* チ ェ ス 盤 等 の 初 期 設 定 。 */ for( i=1; i<=2*n-1; i++ ) { ue[i] = 0; } for( i=2; i<=2*n; i++ ) { sita[i] = 0; } count = 0; /* バ ッ ク ト ラ ッ ク 。 */ perm(1); printf("チ ェ ス 盤 %d× %dの 解 の 数 : %d \n",n,n,count); } } /* 配 置 の 生 成 。 */ void perm(int k) { int i,w; if( k > n ) { /* 解 の 表 示 。 */ count++; printf("[%d]:",count); for( i=1; i<=n; i++ ) { printf("%3d",p[i]); } printf("\n"); return; } - 20 - Nクイー ン問題 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 w = p[k]; for( i=k; i<=n; i++ ) { p[k] = p[i]; p[i] = w; /* マ ス 目 (p[k],k)に ク イ ー ン が 置 け る こ と が わ か っ た の で 置 く 。 */ if( (ue[p[k]-k+n] == ① ue[p[k]-k+n] = ③ ; sita[p[k]+k] = ④ ; )&&(sita[p[k]+k] == ② perm(k+1); /* マ ス 目 (p[k],k)か ら ク イ ー ン を 取 り 除 く 。 */ ue[p[k]-k+n] = ⑤ ; sita[p[k]+k] = ⑥ ; } p[i] = p[k]; } p[k] = w; } 実行結果 % cc nq133.c % ./a.out 5 [1]: 1 3 5 2 4 [2]: 1 4 2 5 3 [3]: 2 4 1 3 5 [4]: 2 5 3 1 4 [5]: 3 1 4 2 5 [6]: 3 5 2 4 1 [7]: 4 2 5 3 1 [8]: 4 1 3 5 2 [9]: 5 2 4 1 3 [10]: 5 3 1 4 2 チ ェ ス 盤 5× 5の 解 の 数 : 10 6 [1]: 2 4 6 1 3 5 [2]: 3 6 2 5 1 4 [3]: 4 1 5 2 6 3 [4]: 5 3 1 6 4 2 チ ェ ス 盤 6× 6の 解 の 数 : 4 0 - 21 - ) ) { Nクイー ン問題 実行時間 実行時間測定 % time ./a.out 14 0 チ ェ ス 盤 14× 14の 解 の 数 : 365596 1.874u 0.000s 0:03.50 53.4% 0+0k 0+8io 0pf+0w % time ./a.out 15 0 チ ェ ス 盤 15× 15の 解 の 数 : 2279184 11.896u 0.000s 0:14.08 84.4% 0+0k 0+8io 0pf+0w % time ./a.out 16 0 チ ェ ス 盤 16× 16の 解 の 数 : 14772512 80.297u 0.000s 1:21.92 98.0% 0+0k 0+8io 0pf+0w - 22 -