题目描述
题目描述
给定一个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
样例输入
4
1 0 1 1
1 1 1 1
1 1 1 1
1 1 1 1
样例输出
0
题目分析
都提示是用搜索了,在这里DFS和BFS应该都可行
使用DFS : 就是找到一个位置放皇后,那就从这开始,递归再去找下一个放皇后的位置
(先不管这个黑白皇后,一步一步来)
等查到地图的末尾,那么就返回,从上一个位置开始继续找
例如 : 在某个位置我找到了放第i个皇后的位置,然后从这开始继续找第i + 1个皇后的位置在某个位置我成功找到了这个位置,那么又继续那样的操作从第i + 1个皇后的位置开始,找第i + 2个皇后的位置,可是,找到地图的末尾也没找到那这次的方法只能return到上次递归的位置之后了(方法弹栈)就又变成了找第i + 1个皇后了,只不过从上次找到放第i + 1个皇后的位置开始,而不是第i个皇后的位置。
使用BFS:emmmm…这个只是猜想,还未实现。可以不看就是遍历整个地图(二维数组),找到一个能放黑皇后的就入队,然后通过这第i个皇后开始,找到所有第i + 1个皇后入队。然后从分别以队列所有黑皇后为head再来从地图头开始找白皇后。最后有几个第n个白皇后入队,那就有几个方法
代码
一步步来吧,从能实现到优化。
最下面那次是初次实现,后面两次只是让代码更简洁,没有实际优化,最后一次找到了能优化的地方
优化前
import java.util.Scanner;
public class Main{
static int sum = 0;
public static void main(String[] args) {
Scanner scan = new Scanner(System.in);
int n = scan.nextInt();
int[][] map = new int[n][n];
//读入地图
for(int i = 0; i < n; i++) {
for (int j = 0; j < n; j++) {
map[i][j] = scan.nextInt();
}
}
boolean isBlack = true;//是否为黑皇后
dfs(1, map, isBlack, 0, 0);
System.out.println(sum);
scan.close();
}
//nums代表着当前皇后的个数。
static void dfs(int nums, int[][] map, boolean isBlack, int x, int y) {
if (nums > map.length) {
if(isBlack) {
dfs(1, map, !isBlack, 0, 0);
}
if(!isBlack) {
sum++;
return;
}
return;
}
for (int i = x; i < map.length; i++) {
for (int j = 0; j < map.length; j++) {
//不是1且同行同列同对角线有相同颜色的皇后就下一个点
if (map[i][j] != 1 || isMatch(map, i, j, isBlack)) {
continue;
}else {
if (isBlack) {
map[i][j] = 2;
}else {
map[i][j] = 3;
}
dfs(nums + 1, map, isBlack, i + 1, j + 1);
map[i][j] = 1;
}
}
}
}
//判断是否符合条件(同列同对角线有同颜色皇后),符合就返回true,反之false
static boolean isMatch(int[][] map, int x, int y, boolean isBlack) {
//这里应该不需要判断下和右是否有,只需判断对角线和左,上。
int i = x;
int j = y;
int flag = isBlack ? 2 : 3;
//判断上边
while(i >= 0) {
if (map[i][y] == flag) {
return true;
}
i--;
}
//判断左边
// while(j >= 0) {
// if (map[x][j] == flag) {
// return true;
// }
// j--;
// }
i = x;
j = y;
//判断左上对角线
while(i >= 0 && j >= 0) {
if (map[i][j] == flag) {
return true;
}
i--;
j--;
}
//判断右上对角线
i = x;
j = y;
while(j < map.length && i >= 0) {
if (map[i][j] == flag) {
return true;
}
i--;
j++;
}
return false;
}
}
优化后
import java.util.Scanner;
public class Main{
static int sum = 0;
static int n;
static int[][] map;
static boolean isBlack = true;//是否为黑皇后,如果为true,map[i][j]设为2,反之为3
public static void main(String[] args) {
Scanner scan = new Scanner(System.in);
n = scan.nextInt();
map = new int[n][n];
//读入地图
for(int i = 0; i < n; i++) {
for (int j = 0; j < n; j++) {
map[i][j] = scan.nextInt();
}
}
dfs(1);
System.out.println(sum);
scan.close();
}
//nums代表着当前皇后的个数
static void dfs(int nums) {
//当放的皇后个数大于n时(map.length就是n)
//就意味着黑皇后或者白皇后满了
//前者需要继续递归白皇后的情况
//后者就可以sum++了
if (nums > n) {
if(isBlack) {
isBlack = false;//现在将要搞白皇后,就需要将isBlack变成false
dfs(1);
isBlack = true;//白皇后考虑完又回溯成黑皇后了,需要将isBlack变成true
}
if(!isBlack) {
sum++;
return;
}
}
for (int i = nums - 1; i < n; i++) {
//能够达成一次次数的条件是每一行都需要有个皇后,
//假设在第三行放第四个皇后那肯定是不可能的,直接return节省时间
//由于nums记录的是第几个皇后,nmus - 1代表行下标,如果行下标小于当前所在行下标
//那就符合上述return要求。
if (nums - 1 < i) {
return;
}
for (int j = 0; j < n; j++) {
//不是1且同行同列同对角线有相同颜色的皇后就下一个点
if (map[i][j] != 1 || isMatch(i, j)) {
continue;
}else {
if (isBlack) {
map[i][j] = 2;
}else {
map[i][j] = 3;
}
//发现可以放皇后那就递归下一行
dfs(nums + 1);
//别玩了回溯时把这个点归为1,毕竟这个点就没被占用了。
map[i][j] = 1;
}
}
}
}
//判断是否符合条件(同列同对角线有同颜色皇后),符合就返回true,反之false
//因为是从上往下,从左往右依次遍历,所以不需要判断右边,右下,左下
//而且由于找到了一点就立马递归下一行,所以不用担心这一行有,所以左边也不需要。
static boolean isMatch(int x, int y) {
//这里应该不需要判断下和右是否有,只需判断对角线和左,上。
int i = x;
int j = y;
int flag = isBlack ? 2 : 3;
//判断上边
while(i >= 0) {
if (map[i][y] == flag) {
return true;
}
i--;
}
i = x;
j = y;
//判断左上对角线
while(i >= 0 && j >= 0) {
if (map[i][j] == flag) {
return true;
}
i--;
j--;
}
//判断右上对角线
i = x;
j = y;
while(j < n && i >= 0) {
if (map[i][j] == flag) {
return true;
}
i--;
j++;
}
return false;
}
}
最后
有哪里写错或者不好的地方,还望指出来。