プロジェクトのgithubアドレス:bitcarmanlee easy-algorithm-interview-and-practice
誰もがスターを付け、メッセージを残し、一緒に学び、進歩することを歓迎します
1.エイトクイーンの問題は何ですか
エイトクイーンの問題はチェスベースの問題です。8つのクイーンを8×8のチェス盤に配置して、1人のクイーンが他のクイーンを直接捕まえることができないようにするにはどうすればよいでしょうか。この目標を達成するために、2つのクイーンを同じ水平線、垂直線、または対角線上に置くことはできません。エイトクイーンの問題は、より一般的なnクイーンの配置の問題に一般化できます。チェス盤のサイズはn×nになり、クイーンの数もnになります。この問題は、n = 1またはn≥4の場合にのみ解決できます。(問題の説明は参考文献1からのものです)
数学の王子ガウスはその年に数え切れないほどの努力を費やし、最終的にエイトクイーン問題の76の解決策があると計算しました。これで、コンピューターシミュレーションを通じてエイトクイーン問題の真の解決策を簡単に計算できます。ガウスをつまずかせる可能性のある計算の複雑さと難しさを想像することができます。
2.ソリューション分析
ガウスは、すでに非常に強力なエイトクイーンの76のソリューションを導き出します。これはガウスのせいにすることはできませんが、エイトクイーンの計算の複雑さは確かに高すぎます。
問題の全体的な考えは依然として暴力的な解決策
です。1。最初にすべての可能なクイーンの配置方法をリストします。
2.配置方法が要件を満たしているかどうかを確認します。
具体的な実装プロセスは次のとおりです。
最初に最初のクイーンを最初の行と最初の列に配置し、次に2番目のクイーンを2番目の行と最初の列に配置して、配置が要件を満たしているかどうかを判断します。明らかにこれは機能しません。同じ列に2つのクイーンがあります。2番目の女王を2番目の行と2番目の列に配置します。これは機能しません。2つの女王は対角線上にあります。現在の条件が満たされるように、2番目のクイーンを2番目の行と3番目の列に配置します。
条件を満たすクイーンがすでに2つあり、3番目のクイーンを配置するか、3行目の最初の列から開始し、条件が満たされない場合は2番目の列、3番目の列を配置します... 8番目のクイーンまで競合しない位置に配置することもできます。現時点で要件を満たすソリューションを見つけてください。
次に、バックトラックを開始し、最初のクイーンを最初の行と2番目の列に配置します。バックトラックが完了し、条件を満たすすべてのソリューションが見つかるまで、後者は上記の方法でループし続けます。
参考文献2には、上記のプロセスをより詳細に説明した写真があり、参考のために投稿しています。次の図は、4つのクイーンのバックトラックプロセスを示しています。原理は8つのクイーンの場合と同じです。
3.コードの実装
上記の原則の分析が完了したら、コードの実装を見てみましょう。
public class NQueueV2 {
public static int N = 4;
public static int[][] boards = new int[N][N];
public static int result = 0;
public static void putQueQue(int k) {
if (k == N) {
result++;
for(int row=0; row<N; row++) {
for(int col=0; col<N; col++) {
System.out.print(boards[row][col] + " ");
}
System.out.println();
}
System.out.println();
} else {
for(int i=0; i<N; i++) {
if (check(k, i)) {
boards[k][i] = 1;
putQueQue(k+1);
boards[k][i] = 0;
}
}
}
}
public static boolean check(int row, int column) {
for(int i=0; i<row; i++) {
if (boards[i][column] == 1) {
return false;
}
}
for(int m=row-1, n=column-1; m>=0 && n >= 0; m--, n--) {
if (boards[m][n] == 1) {
return false;
}
}
for(int m=row-1, n=column+1; m>=0 && n<N; m--, n++) {
if (boards[m][n] == 1) {
return false;
}
}
return true;
}
public static void main(String[] args) {
putQueQue(0);
System.out.println("result is: " + result);
}
}
コード出力結果:
0 1 0 0
0 0 0 1
1 0 0 0
0 0 1 0
0 0 1 0
1 0 0 0
0 0 0 1
0 1 0 0
result is: 2
上記のコードは、最終結果の出力を容易にするために、4つのクイーンをシミュレートし、8つのクイーンはNを8に変更するだけで済みます。
チェス盤をシミュレートする2次元の板の配列で、初期状態は0です。これは、この位置に女王が配置されていないことを意味します。位置が1に設定されている場合、その位置にクイーンがいることを意味します。
kは配置されたクイーンの数を表します。k == Nの場合、N個のクイーンがすべて配置されていることを意味し、この時点で有効な解が得られます。
for(int row=0; row<N; row++) {
for(int col=0; col<N; col++) {
System.out.print(boards[row][col] + " ");
}
System.out.println();
}
コードのこの部分は、有効なソリューションのステータスを出力します。配列内の1の位置は、クイーンがその位置に配置されていることを示します。
コードの他の部分では、iはi番目の列を表し、check(k、i)メソッドは、クイーンを位置(k、i)に配置することが合法かどうかを表します。
for(int i=0; i<row; i++) {
if (boards[i][column] == 1) {
return false;
}
}
コードのこの部分は、2つのクイーンを同じ列に配置できないことを意味します。
for(int m=row-1, n=column-1; m>=0 && n >= 0; m--, n--) {
if (boards[m][n] == 1) {
return false;
}
}
コードのこの部分は、クイーンの左スラッシュにクイーンを含めることができないことを意味します。
for(int m=row-1, n=column+1; m>=0 && n<N; m--, n++) {
if (boards[m][n] == 1) {
return false;
}
}
コードのこの部分は、クイーンの右スラッシュ部分にクイーンを含めることができないことを意味します。
クイーンをこの位置に配置できる場合は、チェス盤の位置を1に設定してから、次の行にクイーンputQueQue(k + 1)を配置します。同時に、バックトラックするときは、位置を0に設定する必要があります。
4.ルーチンの要約
それで、一見複雑に見えるエイトクイーンの問題、図を見て、次にコード分析を見ると、それはそれほど難しいと感じませんか?
本質は、前述の順列と組み合わせに似た、従来のバックトラッキングアルゴリズムの使用です。プロセス全体を理解した後、バックトラックの難しさは、順列と組み合わせの難しさよりもさらに少なくなります!
歴史上最も完全な順列と組み合わせアルゴリズムの詳細な説明とルーチンの要約は、1つの記事で行われます。
参照
1.https://zh.wikipedia.org/wiki/%E5%85%AB%E7%9A%87%E5%90%8E%E9%97%AE%E9%A2%98
2.https:// juejin.cn/post/6844903798926753805