JAVA:N皇后问题-普通方法和最好的位运算方法

public class NQueen { //N皇后:
/*
N皇后问题:给定一个正整数N,在N*N的棋盘上摆N个皇后,返回有多少种摆法
要求:任意二个皇后不能同行、同列、同斜线
*/

public static void main(String[] args) {
	int n=14;
	
	long start1 = System.currentTimeMillis();			//System.currentTimeMillis() 在java中是最常用的获取系统时间的方法---单位:毫秒(秒=毫米/1000)
	System.out.println(nqueen1(n));
	long end1 = System.currentTimeMillis();				//start:开始时间, end:结束时间, 差值:方法系统运行时间(start 和 end 可以修改命名)
	System.out.println("cost timr:" + (end1 - start1) + "ms");		//可以将 差值/1000 ,ms -换- s ,以获得秒的单位
	
	/*
	365596
	cost timr:6008ms
	结论:结果一样,时间复杂度没变,但(常量改进)方法快近25倍
	365596
	cost timr:272ms
	*/
	
	long start2 = System.currentTimeMillis();			//System.currentTimeMillis() 在java中是最常用的获取系统时间的方法---单位:毫秒(秒=毫米/1000)
	System.out.println(nqueen2(n));
	long end2 = System.currentTimeMillis();				//start:开始时间, end:结束时间, 差值:方法系统运行时间(start 和 end 可以修改命名)
	System.out.println("cost timr:" + (end2 - start2) + "ms");		//可以将 差值/1000 ,ms -换- s ,以获得秒的单位
}

//调用实现方法
public static int nqueen1(int n) {
	if(n<1) {
		return 0;						//n<1,没必要执行
	}
	int[] arr = new int[n];				//创建n长度的数组---arr[4]=6: 第五个皇后-5行6列 
	return queen1(arr,0,n);				//调用实现方法
}

										//第一种方法
//实现方法:                arr[4]=6: 第五个皇后-5行6列   i:目前在第i行 
public static int queen1(int[] arr,int i,int n) {
	if(i==n) {			//n(0~n-1),越界-即已经找完一种方法
		return 1;
	}
	int res=0;       	//初始化
	for(int j=0;j<arr.length;j++) {		//尝试每一行的皇后摆在 j 列上是否可行(会尝试所有列-j不可行就尝试j+1)
		if(asd(arr,i,j)) {				//调用分支界限:true则运行---判断当前 i行j列的皇后 是否跟之前的皇后相冲突(行、列、斜线)
			arr[i]=j;					//满足条件,则能在j列摆上i行的皇后
			res+=queen1(arr,i+1,n);		//于是进行i+1皇后摆的列(重新尝试0~n-1个列)
		}
	}
	return res;
}

//内部方法-分支限界:排除出现同列、同行、同斜线的情况
public static boolean asd(int[] arr,int i,int j) {
	for(int k=0;k<i;k++) {				//判断所有情况
		if(j==arr[k] ||  Math.abs(arr[k]-j)==Math.abs(k-i) ) {		//当列相同、同斜线时-输出false(不执行)
			//Math.abs() :返回值的绝对值,注意:当输入值为最小值,依旧返回最小值(int:-2147483648~2147483647 最小值的相反数已溢出,绝对值+1-变-最小值)
			//这里同斜线(即 |j-arr[k]| == |i-k|,如同斜线 arr[8]=10,arr[5]=7, |10-8|=|7-5|)
			return false;						
		}
	}
	return true;
}

										//第二种方法---更快(位运算)
//调用实现方法
public static int nqueen2(int n) {
	if(n<1 || n>32) {					//二进制方法,(字符32位)
		return 0;						//n<1,没必要执行
	}
	int limit = n == 32?-1 : (1<<n)-1;				//创建变量:limit,当N=32时,32个位都是1,即limit=-1,否则将1右移n位数-1(创建出最后有n个1的二进制数)
	//N=8,即 00000000000000000000000011111111, [1<<8-1 : 100000000 -1 : 11111111]
	return queen2(limit,0,0,0);						//调用实现方法
}

/* limit:对n的取值范围进行限制,n=5, limit=11111
   colLim:列的限制(哪个行、列有皇后+1,然后这个列其他行就不能+1)
   leftDiaLim:左斜线的限制(当第一行其中一个列+1,它的左下所有行的斜线都+1,其他行同理)
   实现方法:如第一行(00010000),左n行:(00010000)<<n
   rightDiaLim:右斜线的限制(当第一行其中一个列+1,它的右下所有行的斜线都+1,其他行同理)
 */
public static int queen2(int limit,int colLim,int leftDiaLim,int rightDiaLim) {
	if(colLim == limit) {			//即所有列都成功完成皇后选择递归就退出
		return 1;
	}
	
	// 它= colLim | leftDiaLim | rightDiaLim  :即将所有位为1的集合在一起, |(或):有1即为1  ,&(并):都为1即为1  ,~(非):0则为1 
	//limit &  它  ,即只保留最后n个位置的1
	int pos = limit &( ~(colLim | leftDiaLim | rightDiaLim) );				//取所有位上不为1的位置-(不在同列、不在斜线上)(作为候补皇后位置)
	int mostRightOne = 0;
	int res = 0;
	
	while(pos != 0) {														//列上还有皇后位置可选
		mostRightOne = pos & (~pos +1);										//只提取出Pos最右1的位
		pos = pos - mostRightOne;											//提取出最右1之后的pos
		res +=queen2(limit,colLim | mostRightOne,(leftDiaLim | mostRightOne)<<1, (rightDiaLim | mostRightOne)>>>1);
		//	colLim | mostRightOne: 原来列限制 或(+) 现在放皇后列的限制
		//(leftDiaLim | mostRightOne)<<1:原来左斜线限制 或 现在放皇后之后左斜线限制
		// (rightDiaLim | mostRightOne) >>>1:原来右斜线限制 或 现在放皇后之后右斜线限制
	}
	return res;
}

}

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值