2n皇后問題題目描述 :
給定一個n*n的棋盤,棋盤中有一些位置不能放皇后。現在要向棋盤中放入n個黑皇后和n個白皇后,使任意的兩個黑皇后都不在同一行、同一列或同一條對角線上,任意的兩個白皇后都不在同一行、同一列或同一條對角線上 。問總共有多少种放法?n小於等於8。
輸入格式 :
輸入的第一行爲一個整數n,表示棋盤的大小。接下來n行,每行n個0或1的整數,如果一個整數爲1,表示對應的位置可以放皇后,如果一個整數爲0,表示對應的位置不可以放皇后。
樣例輸入 :
4
1 1 1 1
1 1 1 1
1 1 1 1
1 1 1 1
輸出 : 2
問題分析:在分析 2n皇后 問題前,我們先來看看此問題的另一個版本: n皇后問題
顧名思義,所謂的2n皇后問題,無非在n皇后的基礎上增加了n個另一種顏色皇后。且兩種顏色皇后互不影響。所以n皇后問題只要有n個同色皇后滿足條件便可。
上圖 :
現在我們可以設想:同樣大小的棋盤,如果解得全部n皇后問題的解集,那麼這些解集中只要有兩種放法中皇后落子位置沒有重複,那麼就可以得到兩種2n皇后的解法(顏色互換有兩種放法)。
所以說,只要解決了n皇后問題,那麼2n皇后問題便迎刃而解,且能去除多放n個皇后的運算和判斷
N皇后問題
分析:
n*n規格的棋盤要放下n個皇后,那麼說明每一行必須且僅能落下一個皇后。
這時候我們便能使用深度優先搜索算法去組合出所有的排列方式,並對每一步落子位置加以判斷。
使其滿足規則。
深度優先搜索算法 的本質便是是遞歸。遞歸函數部分代碼如下:
static public void dfs(int line,int[][] temp) { //該次搜索行數,輸入數據二維數組
if(line<n) { //遞歸函數出口
for(int i=0;i<n;i++) {
if(temp[line][i]==1&&check(line,i)) { //檢查該位置是否能落子,並判斷落子後是否滿足規則
flag[line][i] = 1; //flag數組表示棋盤落子位置,1代表皇后
dfs(line+1,temp); //進行下一行搜索
flag[line][i]=0; //回溯
}
}
}else {
StringBuilder sb = new StringBuilder(); //得到一種解法,將其位置信息以字符串形式保存在
for(int i=0;i<n;i++) { //list集合中。
for(int j=0;j<n;j++) {
if(flag[i][j]==1)
sb.append(""+j);
}
}
list.add(sb.toString());
}
}
這樣便能得到了所有的n皇后問題的解集,並將其位置信息保存在了ArrayList<string list集合中。(泛型<>裏打的String爲什麼會消失?)
然後我們去兩兩對比解集中的元素,只要每一位字符都不相同。便能組合成兩種2n皇后問題的解法。
比較函數代碼如下:
for(int i=0;i<list.size();i++) {
for(int j=i+1;j<list.size();j++) {
if(eqs(list.get(i),list.get(j)))
num+=2;
}
}
static public boolean eqs(String s1,String s2) {
for(int i=0;i<s1.length();i++) {
if(s1.charAt(i)==s2.charAt(i))
return false;
}
return true;
}
解題完整代碼:
import java.util.ArrayList;
import java.util.Scanner;
public class the2n皇后問題 {
static int n;
static int[][] flag;
static ArrayList<String> list = null;
public static void main(String[] args) {
Scanner s = new Scanner(System.in);
n = s.nextInt();
flag = new int[n][n]; //落子標記數組
int[][] temp = new int[n][n];
for(int i=0;i<n;i++) {
for(int j=0;j<n;j++) {
temp[i][j]= s.nextInt();
}
}
list = new ArrayList<String>();
dfs(0,temp);
int num = 0;
for(int i=0;i<list.size();i++) {
for(int j=i+1;j<list.size();j++) {
if(eqs(list.get(i),list.get(j)))
num+=2;
}
}
System.out.println(num);
}
static public void dfs(int line,int[][] temp) { //該次搜索行數,輸入數據二維數組
if(line<n) { //遞歸函數出口
for(int i=0;i<n;i++) {
if(temp[line][i]==1&&check(line,i)) { //檢查該位置是否能落子,並判斷落子後是否滿足規則
flag[line][i] = 1; //flag數組表示棋盤落子位置,1代表皇后
dfs(line+1,temp); //進行下一行搜索
flag[line][i]=0; //回溯
}
}
}else {
StringBuilder sb = new StringBuilder(); //得到一種解法,將其位置信息以字符串形式保存在
for(int i=0;i<n;i++) { //list集合中。
for(int j=0;j<n;j++) {
if(flag[i][j]==1)
sb.append(""+j);
}
}
list.add(sb.toString());
}
}
public static boolean check(int line ,int row) {
int l = line; int r = row;
while(--l>=0&&--r>=0) { //左上對角線檢查
if(flag[l][r]==1)
return false;
}
l = line; r = row;
while(--l>=0&&++r<n) { //右上對角線檢查
if(flag[l][r]==1)
return false;
}
while(--line>=0) {
if(flag[line][row]==1) //上方檢查
return false;
}
return true;
}
static public boolean eqs(String s1,String s2) {
for(int i=0;i<s1.length();i++) {
if(s1.charAt(i)==s2.charAt(i))
return false;
}
return true;
}
}
運行結果截圖 :
歡迎大家提出改進意見,互相交流。
有疑問可留言。