[蓝桥杯] BASIC-27 VIP 2N皇后问题——[Java实现]

题目描述

题目描述
给定一个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;
	}
}

最后

有哪里写错或者不好的地方,还望指出来。

  • 2
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 5
    评论
评论 5
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值