蓝桥杯基础练习 2n皇后问题(Java)(初试搜索、回溯)(含递归)

试题 基础练习 2n皇后问题

资源限制
时间限制:1.0s 内存限制:512.0MB

问题描述
  给定一个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

【注意】
①其实后续judge_black和judge_white这两个对黑皇后和白皇后位置关系是否合理的判断方法,是可以合在一起的,在传入参数时多加一个“2–黑”或者“3–白”即可。
②不一定要等到最后一行的黑皇后放好,再对整盘的黑皇后做判断。可以在第二行的时候就每走一步判断一次。
比如说,第一个黑皇后放在(0,0),那么递归后第二行会在(1,0)开始放第二个黑皇后,但是此时两个黑皇后已经在同一列,不满足题意,若先调用judge方法判断,可以免了该情况下第三行和第四行的递归调用,节省了许多运行时间,回溯的时候也会简单一点。

import java.util.Scanner;
public class Main{
	static int sum=0;
	static int n;
	public static void main(String[] args){
		Scanner input=new Scanner(System.in);
		n = input.nextInt();
		int[][] arr = new int[n][n];
		int i,j;
		//输入棋盘
		for(i=0;i<n;i++){
			for(j=0;j<n;j++){
				arr[i][j] = input.nextInt();
			}
		}
		find_black(arr,0);
		System.out.print(sum);
	}
	//判断黑皇后合理的放法,再调用函数判断白皇后
	public static void find_black(int[][] arr,int a){
		for(int i=0;i<n;i++){
			//遇到0则跳过
			if(arr[a][i]==0) continue;
			//否则放入黑皇后(2代表黑皇后)
			arr[a][i]=2;
			//递归,逐层调用find_black使棋盘各行获得一个黑皇后
			if(a<n-1) find_black(arr,a+1);
			//当到最后一行时
			if(a==n-1){
				//判断此时n个黑皇后摆放位置是否满足要求
				int b=judge_black(arr,n);
				//若满足要求
				if(b==1){
					//调用find_white放入白皇后
					find_white(arr,0);
					//抹除所有白皇后的痕迹
					for(int j=0;j<n;j++){
						for(int k=0;k<n;k++){
							if(arr[j][k]==3) arr[j][k]=1;
							}
						}
				}
			}
			//抹除部分黑皇后--即:回溯上一步
			for(int j=a;j<n;j++){
				for(int k=0;k<n;k++){
					if(arr[j][k]==2){
						arr[j][k]=1;
						break;
					}
				}
			}
		}
	}
	//加入白皇后后,计算合理放法的个数
	public static void find_white(int[][] arr,int a){
		for(int i=0;i<n;i++){
			//若遇到0和黑皇后则跳过
			if(arr[a][i]==2 || arr[a][i]==0) continue;
			//否则放入白皇后
			arr[a][i]=3;
			//递归,每行都放入一个白皇后
			if(a<n-1) find_white(arr,a+1);
			//当放好最后一行时
			if(a==n-1){
				//判断白皇后的摆放位置是否满足要求
				int b=judge_white(arr,n);
				//若是,sum++,即方法+1
				if(b==1) sum++;
			}
			//回溯———抹除最后一步白皇后的痕迹
			for(int j=a;j<n;j++){
				for(int k=0;k<n;k++){
					if(arr[j][k]==3) arr[j][k]=1;
				}
			}
		}
	}
	//判断黑皇后是否全部不在同一行、列、对角线
	public static int judge_black(int[][] arr,int n){
		int i,j,a=0,b=0;
		//行,列
		for(i=0;i<n;i++){
			for(j=0;j<n;j++){
				if(arr[i][j]==2) a++;
				if(arr[j][i]==2) b++;
			}
			if(a>=2 || b>=2) return 0;
			a=0;
			b=0;
		}
		//对角线
		//将放黑皇后的位置储存在arr1数组中
		a=0;
		int[][] arr2 = new int[n][2];
		for(i=0;i<n;i++){
			for(j=0;j<n;j++){
				if(arr[i][j]==2){
					arr2[a][0]=i;
					arr2[a++][1]=j;
				}
			}
		}
		//若存在两个黑皇后的行数之差==列数之差,或者行列之和相等,则其处于同一对角线
		for(i=0;i<n-1;i++){
			for(j=i+1;j<n;j++){
				if(arr2[i][0]-arr2[j][0] == arr2[i][1]-arr2[j][1]) return 0;
				if(arr2[i][0]+arr2[i][1]-arr2[j][0]-arr2[j][1]==0) return 0;
			}
		}
		//若不在同行、列、对角线,则满足要求,返回1
		return 1;
	}
	//与上面judge_black方法雷同,其实在传参数时引入黑--2、白--3,即可将两个方法合二为一
	public static int judge_white(int[][] arr,int n){
		int i,j,a=0,b=0;
		//行,列
		for(i=0;i<n;i++){
			for(j=0;j<n;j++){
				if(arr[i][j]==3) a++;
				if(arr[j][i]==3) b++;
			}
			if(a>=2 || b>=2) return 0;
			a=0;
			b=0;
		}
		//对角线
		//将放白皇后的位置储存在arr1数组中
		b=0;
		int[][] arr3 = new int[n][2];
		for(i=0;i<n;i++){
			for(j=0;j<n;j++){
				if(arr[i][j]==3){
					arr3[b][0]=i;
					arr3[b++][1]=j;
				}
			}
		}
		//若存在两个白皇后的行数之差==列数之差,或者行列之和相等,则其处于同一对角线
		for(i=0;i<n-1;i++){
			for(j=i+1;j<n;j++){
				if(arr3[i][0]-arr3[j][0] == arr3[i][1]-arr3[j][1]) return 0;
				if(arr3[i][0]+arr3[i][1]-arr3[j][0]-arr3[j][1]==0) return 0;
			}
		}
		//若不在同行、列、对角线,则满足要求,返回1
		return 1;
	}
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值