八皇后问题:
在8*8格的国际象棋棋盘上放8个皇后,使其不能攻击
即:任意两个皇后都不能处于同一行,同一列,或同一斜线上,共有几种解法?
思考:
如果一行一行的放,每放1个皇后,就要判断是否和前边每一行冲突,即是否在同一列或同一斜线上。判断两皇后不在同一列--列不相等
0,0 | 0,1 | 0,2 |
1,0 | 1,1 | 1,2 |
2,0 | 2,1 | 2,2 |
如上图,判断不在同一斜线上--皇后行差的绝对值不能等于列差的绝对值
假如我在坐标(0,0)处放置第一个皇后,在(1,1)处放置第二个皇后,这时第一个皇后的行数为0,减去第二个皇后行数1,得到的结果的绝对值等于列数之差的绝对值,故两皇后在同一斜线。从右上指向左下的斜线同理。
知道了怎么判断一个皇后是否与前面皇后冲突,之后只要在每一行遍历的放置皇后,如果当前位置与前面任何一个皇后的位置冲突就跳过本次循环进行下一次循环。如果遍历完了,没有任何一个位置满足要求就回溯。
用一维数组int[] map = new int[8];来存储一个正确的方案,对应关系为 map[行数-1]=列数-1
判断是否冲突的代码如下
//i,j为当前皇后的位置
public boolean ifLegal(int i, int j, int[] map){//判断当前皇后的位置是否与其他皇后冲突
//判断两皇后不在同一斜线上--皇后行差的绝对值不能等于列差的绝对值
//判断不在同一列--列不相等
if(i == 0){
return true;
}else{
for(int k = 0;k < i;k++){
if(j == map[k] || (i-k) == Math.abs(j-map[k])){
return false;//一行一行的放,遍历已经放过皇后的行,如果前面几行与目前的皇后位置冲突,返回false
}//这里不能把true写在这层的else里,要求遍历中全不冲突才能返回true,如果写进else里只要有一行不冲突就会返回true
}
}
return true;
}
将解法写入数组的大致思路:
在每一行,尝试将皇后放在每一列,如果不冲突则将位置写入数组,如果冲突就进行下一次循环,如果没有符合标准的位置就回溯。判断当前正在放置的行数,如果已经放到了第8行即map[7],说明已经找到了一个解,这时打印一次数组,得到一个方案。
代码:
public void findSolution(int i, int[] map){
for(int k = 0;k < 8;k++){
if(ifLegal(i, k, map)){//不冲突的位置,直接写入数组
map[i] = k;
}else{
continue;//冲突的话就进行下一次循环,即:列+1
}
if(i < 7){
findSolution(i+1, map);
}else if(i == 7){//i = 7时说明 已经放到了最后一行,即完成了一次方案,这时打印一次数组得到一次结果
count++;
cheakMap(map);
}
}
}
public void cheakMap(int[] map){//打印数组
System.out.print("第" + count + "个方案:");
for(int i = 0;i < map.length;i++){
System.out.print(map[i]+1 + " ");
}
System.out.println();
}
其中count为一个全局变量,记录解的个数。