【问题描述】在一个8×8格子的棋盘上,要布局八个皇后,条件是不能出现两个皇后在同一行或同一列或同一斜线的情况,以防“相互攻击”,请问共有多少种合理的布局方式,每一种布局的具体情况如何? 【分析】显然问题的键在于如何判定某个皇后所在的行、列、斜线上是否有别的皇后;可以从矩阵的特点上找到规律,如果在同一行,则行号相同;如果在同一列上,则列号相同;如果同在/ 斜线上的行列值之和相同;如果同在\斜线上的行列值之差相同;如果斜线不分方向,则同一斜线上两皇后的行号之差的绝对值与列号之差的绝对值相同。从下图可以验证上面的说法: 对于一组布局我们可以用一个一维数组来表示:X:ARRAY [1..8] OF INTEGER;X[I]的下标I表示第I个皇后在棋盘的第I行,X[I]的内容表示在第I行的第X[I]列,例如:X[3]=5就表示第3个皇后在第3 行的第5列。在这种方式下,要表示两个皇后A和B不在同一列或斜线上的条件可以描述为:X[A]<>X[B] AND ABS(A-B)<>ABS(X[A]-X[B]){A和B分别表示两个皇后的行号} 【解法】有了上面的分析,相信大家可以找到不同的算法,这里我们提供三种,当然稍加改动就可推广到N皇后问题 PROGRAM QN(INPUT,OUTPUT);{递归算法} CONST N=8; VAR CONT,I:INTEGER; A:ARRAY[1..N] OF BYTE;{存放正确的一组解} C:ARRAY[1..N] OF BOOLEAN;{存放某一列放皇后的情况,用于判断是否有同列的情况} L:ARRAY[1-N..N-1] OF BOOLEAN;{存放某一斜线上放皇后的情况,用于判断是否有同斜线的情况;斜线的方向为\} R:ARRAY[2..2*N] OF BOOLEAN;{存放某一斜线上放皇后的情况,用于判断是否有同斜线的情况;斜线的方向为/} PROCEDURE PR; VAR I:INTEGER; BEGIN FOR I:=1 TO N DO WRITE(A[I]:4); INC(CONT); WRITELN(" CONT=",CONT); END; PROCEDURE TRY(I:INTEGER); VAR J:INTEGER; PROCEDURE ERASE(I:INTEGER); BEGIN C[J]:=TRUE; L[I-J]:=TRUE; R[I+J]:=TRUE; END; BEGIN FOR J:=1 TO N DO BEGIN IF C[J] AND L[I-J] AND R[I+J] THEN BEGIN A[I]:=J; C[J]:=FALSE; L[I-J]:=FALSE; R[I+J]:=FALSE; IF I<N THEN TRY(I+1) ELSE PR; ERASE(I); END; END; END;BEGIN FOR I:=1 TO N DO C[I]:=TRUE; FOR I:=1-N TO N-1 DO L[I]:=TRUE; FOR I:=2 TO 2*N DO R[I]:=TRUE; CONT:=0; I:=1; TRY(I); WRITELN; WRITELN("PROGRAM END."); READLN; END.PROGRAM HUANGHOU(INPUT,OUTPUT);{迭代回溯算法} CONST N=8; VAR K:INTEGER; X:ARRAY[1..N] OF INTEGER; FUNCTION PLACE(K:INTEGER):BOOLEAN; VAR I:INTEGER; BEGIN I:=1; WHILE I<K DO BEGIN IF (ABS(X[I]-X[K])=ABS(I-K)) OR (X[I]=X[K]) THEN BEGIN PLACE:=FALSE; EXIT; END; I:=I+1; END; PLACE:=TRUE; END; PROCEDURE PRN; VAR I:INTEGER; BEGIN FOR I:=1 TO N DO WRITE(X[I]:4); WRITELN; END; PROCEDURE NQUEENS(N:INTEGER); BEGIN X[1]:=0; K:=1; WHILE K>0 DO BEGIN X[K]:=X[K]+1; WHILE ((X[K]<=N) AND (NOT PLACE(K))) DO X[K]:=X[K]+1; IF X[K]<=N THEN BEGIN IF K=N THEN PRN ELSE BEGIN K:=K+1; X[K]:=0; END; END ELSE K:=K-1; END; END;BEGIN NQUEENS(N); READLN; END.program bahh;{递归回溯算法} var stack:array[1..20] of byte; n,total:integer; procedure make(l:integer); var i,j:integer; att:boolean; begin if l=n+1 then begin inc(total); writeln("No:",total); for j:=1 to n do writeln(" ",stack[j],"*"); writeln; exit; end; for i:=1 to n do begin att:=false; stack[l]:=i; for j:=1 to l-1 do if(abs(l-j)=abs(stack[j]-i))or(i=stack[j]) then begin att:=true; j:=l-1; end; if not att then make(l+1); end; end; begin total:=0; fillchar(stack,sizeof(stack),0); write("n="); readln(n); make(1); writeln("Total=":9,total); readln; end.PROGRAM BHH(INPUT,OUTPUT);{穷举算法:最好理解,但效率最低} CONST N=8; VAR I1,I2,I3,I4,I5,I6,I7,I8:INTEGER; X:ARRAY [1..N] OF INTEGER; PROCEDURE PRINT;{输出正确的解} VAR I:INTEGER; BEGIN FOR I:=1 TO N DO WRITE(X[I]); WRITELN; END; FUNCTION CHECK():BOOLEAN; VAR A,B:INTEGER; BEGIN FOR A:=2 TO N DO BEGIN FOR B:=1 TO A-1 DO BEGIN IF (ABS(X[A]-X[B])=ABS(A-B)) OR (X[A]=X[B]) THEN BEGIN CHECK:=FALSE; EXIT; END; END; END; CHECK:=TRUE; END;BEGIN FOR I1:=1 TO N DO FOR I2:=1 TO N DO FOR I3:=1 TO N DO FOR I4:=1 TO N DO FOR I5:=1 TO N DO FOR I6:=1 TO N DO FOR I7:=1 TO N DO FOR I8:=1 TO N DO BEGIN X[1]:=I1; X[2]:=I2; X[3]:=I3; X[4]:=I4; X[5]:=I5; X[6]:=I6; X[7]:=I7; X[8]:=I8; IF CHECK() THEN PRINT; END; READLN; END.
public class Queen { // 同栏是否有皇后,1表示有 private int[] column; // 右上至左下是否有皇后 private int[] rup; // 左上至右下是否有皇后 private int[] lup; // 解答 private int[] queen; // 解答编号 private int num; public Queen() { column = new int[8+1]; rup = new int[2*8+1]; lup = new int[2*8+1]; for(int i = 1; i <= 8; i++) column[i] = 1; for(int i = 1; i <= 2*8; i++) rup[i] = lup[i] = 1; queen = new int[8+1]; } public void backtrack(int i) { if(i > 8) { showAnswer(); } else { for(int j = 1; j <= 8; j++) { if(column[j] == 1 && rup[i+j] == 1 && lup[i-j+8] == 1) { queen[i] = j; // 设定为占用 column[j] = rup[i+j] = lup[i-j+8] = 0; backtrack(i+1); column[j] = rup[i+j] = lup[i-j+8] = 1; } } } } protected void showAnswer() { num++; System.out.println("\n解答 " + num); for(int y = 1; y <= 8; y++) { for(int x = 1; x <= 8; x++) { if(queen[y] == x) { System.out.print(" Q"); } else { System.out.print(" ."); } } System.out.println(); } } public static void main(String[] args) { Queen queen = new Queen(); queen.backtrack(1); }}
public class Queen { // 同栏是否有皇后,1表示有 private int[] column; // 右上至左下是否有皇后 private int[] rup; // 左上至右下是否有皇后 private int[] lup; // 解答 private int[] queen; // 解答编号 private int num; public Queen() { column = new int[8 + 1]; rup = new int[2 * 8 + 1]; lup = new int[2 * 8 + 1]; for (int i = 1; i <= 8; i++) column[i] = 1; for (int i = 1; i <= 2 * 8; i++) rup[i] = lup[i] = 1; queen = new int[8 + 1]; } public void backtrack(int i) { if (i > 8) { showAnswer(); } else { for (int j = 1; j <= 8; j++) { if (column[j] == 1 && rup[i + j] == 1 && lup[i - j + 8] == 1) { queen[i] = j; // 设定为占用 column[j] = rup[i + j] = lup[i - j + 8] = 0; backtrack(i + 1); column[j] = rup[i + j] = lup[i - j + 8] = 1; } } } } protected void showAnswer() { num++; System.out.println("\n解答 " + num); for (int y = 1; y <= 8; y++) { for (int x = 1; x <= 8; x++) { if (queen[y] == x) { System.out.print(" Q"); } else { System.out.print(" ."); } } System.out.println(); } } public static void main(String[] args) { Queen queen = new Queen(); queen.backtrack(1); } }
【问题描述】在一个8×8格子的棋盘上,要布局八个皇后,条件是不能出现两个皇后在同一行或同一列或同一斜线的情况,以防“相互攻击”,请问共有多少种合理的布局方式,每一种布局的具体情况如何?
【分析】显然问题的键在于如何判定某个皇后所在的行、列、斜线上是否有别的皇后;可以从矩阵的特点上找到规律,如果在同一行,则行号相同;如果在同一列上,则列号相同;如果同在/ 斜线上的行列值之和相同;如果同在\斜线上的行列值之差相同;如果斜线不分方向,则同一斜线上两皇后的行号之差的绝对值与列号之差的绝对值相同。从下图可以验证上面的说法: 对于一组布局我们可以用一个一维数组来表示:X:ARRAY [1..8] OF INTEGER;X[I]的下标I表示第I个皇后在棋盘的第I行,X[I]的内容表示在第I行的第X[I]列,例如:X[3]=5就表示第3个皇后在第3 行的第5列。在这种方式下,要表示两个皇后A和B不在同一列或斜线上的条件可以描述为:X[A]<>X[B] AND ABS(A-B)<>ABS(X[A]-X[B]){A和B分别表示两个皇后的行号}
【解法】有了上面的分析,相信大家可以找到不同的算法,这里我们提供三种,当然稍加改动就可推广到N皇后问题
PROGRAM QN(INPUT,OUTPUT);{递归算法}
CONST N=8;
VAR
CONT,I:INTEGER;
A:ARRAY[1..N] OF BYTE;{存放正确的一组解}
C:ARRAY[1..N] OF BOOLEAN;{存放某一列放皇后的情况,用于判断是否有同列的情况}
L:ARRAY[1-N..N-1] OF BOOLEAN;{存放某一斜线上放皇后的情况,用于判断是否有同斜线的情况;斜线的方向为\}
R:ARRAY[2..2*N] OF BOOLEAN;{存放某一斜线上放皇后的情况,用于判断是否有同斜线的情况;斜线的方向为/} PROCEDURE PR;
VAR
I:INTEGER;
BEGIN
FOR I:=1 TO N DO WRITE(A[I]:4);
INC(CONT);
WRITELN(" CONT=",CONT);
END; PROCEDURE TRY(I:INTEGER);
VAR
J:INTEGER; PROCEDURE ERASE(I:INTEGER);
BEGIN
C[J]:=TRUE;
L[I-J]:=TRUE;
R[I+J]:=TRUE;
END;
BEGIN
FOR J:=1 TO N DO
BEGIN
IF C[J] AND L[I-J] AND R[I+J] THEN
BEGIN
A[I]:=J;
C[J]:=FALSE;
L[I-J]:=FALSE;
R[I+J]:=FALSE;
IF I<N THEN TRY(I+1) ELSE PR;
ERASE(I);
END;
END;
END;BEGIN
FOR I:=1 TO N DO C[I]:=TRUE;
FOR I:=1-N TO N-1 DO L[I]:=TRUE;
FOR I:=2 TO 2*N DO R[I]:=TRUE;
CONT:=0;
I:=1;
TRY(I);
WRITELN;
WRITELN("PROGRAM END.");
READLN;
END.PROGRAM HUANGHOU(INPUT,OUTPUT);{迭代回溯算法}
CONST N=8;
VAR
K:INTEGER;
X:ARRAY[1..N] OF INTEGER; FUNCTION PLACE(K:INTEGER):BOOLEAN;
VAR
I:INTEGER;
BEGIN
I:=1;
WHILE I<K DO
BEGIN
IF (ABS(X[I]-X[K])=ABS(I-K)) OR (X[I]=X[K]) THEN
BEGIN
PLACE:=FALSE;
EXIT;
END;
I:=I+1;
END;
PLACE:=TRUE;
END; PROCEDURE PRN;
VAR
I:INTEGER;
BEGIN
FOR I:=1 TO N DO WRITE(X[I]:4);
WRITELN;
END; PROCEDURE NQUEENS(N:INTEGER);
BEGIN
X[1]:=0;
K:=1;
WHILE K>0 DO
BEGIN
X[K]:=X[K]+1;
WHILE ((X[K]<=N) AND (NOT PLACE(K))) DO X[K]:=X[K]+1;
IF X[K]<=N THEN
BEGIN
IF K=N THEN PRN
ELSE
BEGIN
K:=K+1;
X[K]:=0;
END;
END
ELSE K:=K-1;
END;
END;BEGIN
NQUEENS(N);
READLN;
END.program bahh;{递归回溯算法}
var
stack:array[1..20] of byte;
n,total:integer; procedure make(l:integer);
var
i,j:integer;
att:boolean;
begin
if l=n+1 then
begin
inc(total);
writeln("No:",total);
for j:=1 to n do writeln(" ",stack[j],"*");
writeln;
exit;
end;
for i:=1 to n do
begin
att:=false;
stack[l]:=i;
for j:=1 to l-1 do
if(abs(l-j)=abs(stack[j]-i))or(i=stack[j]) then
begin
att:=true;
j:=l-1;
end;
if not att then make(l+1);
end;
end;
begin
total:=0;
fillchar(stack,sizeof(stack),0);
write("n=");
readln(n);
make(1);
writeln("Total=":9,total);
readln;
end.PROGRAM BHH(INPUT,OUTPUT);{穷举算法:最好理解,但效率最低}
CONST N=8;
VAR
I1,I2,I3,I4,I5,I6,I7,I8:INTEGER;
X:ARRAY [1..N] OF INTEGER; PROCEDURE PRINT;{输出正确的解}
VAR
I:INTEGER;
BEGIN
FOR I:=1 TO N DO WRITE(X[I]);
WRITELN;
END; FUNCTION CHECK():BOOLEAN;
VAR
A,B:INTEGER;
BEGIN
FOR A:=2 TO N DO
BEGIN
FOR B:=1 TO A-1 DO
BEGIN
IF (ABS(X[A]-X[B])=ABS(A-B)) OR (X[A]=X[B]) THEN
BEGIN
CHECK:=FALSE;
EXIT;
END;
END;
END;
CHECK:=TRUE;
END;BEGIN
FOR I1:=1 TO N DO
FOR I2:=1 TO N DO
FOR I3:=1 TO N DO
FOR I4:=1 TO N DO
FOR I5:=1 TO N DO
FOR I6:=1 TO N DO
FOR I7:=1 TO N DO
FOR I8:=1 TO N DO
BEGIN
X[1]:=I1;
X[2]:=I2;
X[3]:=I3;
X[4]:=I4;
X[5]:=I5;
X[6]:=I6;
X[7]:=I7;
X[8]:=I8;
IF CHECK() THEN PRINT;
END;
READLN;
END.
private int[] column; // 右上至左下是否有皇后 private int[] rup; // 左上至右下是否有皇后 private int[] lup; // 解答 private int[] queen; // 解答编号 private int num; public Queen() {
column = new int[8 + 1];
rup = new int[2 * 8 + 1];
lup = new int[2 * 8 + 1];
for (int i = 1; i <= 8; i++)
column[i] = 1;
for (int i = 1; i <= 2 * 8; i++)
rup[i] = lup[i] = 1;
queen = new int[8 + 1];
} public void backtrack(int i) {
if (i > 8) {
showAnswer();
} else {
for (int j = 1; j <= 8; j++) {
if (column[j] == 1 && rup[i + j] == 1 && lup[i - j + 8] == 1) {
queen[i] = j; // 设定为占用
column[j] = rup[i + j] = lup[i - j + 8] = 0;
backtrack(i + 1);
column[j] = rup[i + j] = lup[i - j + 8] = 1;
}
}
}
} protected void showAnswer() {
num++;
System.out.println("\n解答 " + num);
for (int y = 1; y <= 8; y++) {
for (int x = 1; x <= 8; x++) {
if (queen[y] == x) {
System.out.print(" Q");
} else {
System.out.print(" .");
}
}
System.out.println();
}
} public static void main(String[] args) {
Queen queen = new Queen();
queen.backtrack(1);
}
}