递归——N皇后问题

/*
N 皇后问题
N皇后问题是指在N*N的棋盘上要摆N个皇后
要求任何两个皇后不同行,不同列,也不在同一条斜线上

给定一个整数n,返回n皇后的摆法有多少种
n = 1,返回1
n = 2或3,2皇后和3皇后问题无论怎么摆放都不行,返回0
n = 8返回92
*/

/*
   [ 1 ] 1*1  能 或 不能(讨论两个皇后的问题)
 2皇后无解  0
   [ ] [ ]
   [ ] [ ]

 3皇后无解 0
   [ ] [ ] [ ]
   [ ] [ ] [ ]
   [ ] [ ] [ ]

 4皇后2个解
   [ ][1][ ][ ]
   [ ][ ][ ][2]
   [3][ ][ ][ ]
   [ ][ ][4][ ]
                       反转  得到第二种解法
   [ ][ ][1][ ]
   [2][ ][ ][ ]
   [ ][ ][ ][3]
   [ ][4][ ][ ]
 */


func TestQueenNum1(t *testing.T) {
	fmt.Println(QueenNum1(100))
}

func QueenNum1(n int) int { //最好尝试方法,但是可以加速常数项
	if n < 1 {
		return 0
	}
	// record[0] ? record[1] ? record[2]
	record := make([]int, n) //record[i] -> i行的皇后,放在了第几列
	return processQueen1(0,record,n)
}


//潜台词:record[0...i-1]  (0,i-1)的皇后都存在record里,任何两个皇后一定都不共行、不共列、不共斜线
//目前来到第i行
//record[0...i-1]表示之前的行,放了的皇后位置
//n代表整体一共有多少行  0 ~ n-1 行有效  到了n 就越界了需要停
//返回值时,摆完所有的皇后,合理的摆法有多少种
func processQueen1(i int, record []int,n int) int {
	if i == n {  //终止行
		return 1  //上面的状况 1种
	}
	//没有到终止为止,还有皇后要摆
	res := 0
	for j := 0; j < n; j++ { //当前行在i行,尝试i行所有的列 -> j
		//当前i行的皇后,放在j列,会不会和之前(0,i-1)的皇后,不共行、列、斜线
		//如果是,认为有效
		//如果不是,认为无效
		if isValid(record,i,j) {
			record[i] = j
			res += processQueen1(i+1,record,n)
		}
	}
	return res
}

// record[0...i-1]你需要看,record[i...]不需要看
// 返回i行皇后,放在了j列,是否有效
func  isValid(record []int, i, j int) bool  {
   for k := 0; k < i; k++ { //之前某个k行的皇后
	   if j == record[k] || Abs(record[k] - j) == Abs(i-k) {
		   return false
	   }
   }
	return true
}

func Abs(a int) int {
	if a < 0 {
		return -a
	}
	return a
}

// a行 b列   c行 d 列  两个皇后 冲突 有什么样的关系
// 肯定不会在行上打架
// 共列:   b == d
// 共斜线: |a-c|  == |b-d|

func AbsI32(i int32) int32 {
	if i < 0 {
		return -i
	}
	return i
}

//优化常数项,位运算

func TestQueenNum2(t *testing.T)  {
	for i := 0; i <= 32; i ++ {
		if QueenNum1(i) != int(AbsI32(QueenNum2(int32(i)))) {
			fmt.Println(i)
			return
		}
	}
	fmt.Println("pass")
}
// 请不要超过32皇后问题

func QueenNum2(n int32) int32 {
	if n < 1 || n > 32 {
		return 0
	}
	var limit int32 = (1 << n) - 1 // >= 0 && < 32
	if n == 32 {
		limit = -1
	}

	return processQueen2(limit,0,0,0)
}

// colLim 列的限制,1的位置不能放皇后,0的位置可以
// leftDiaLim 左斜线的限制,1的位置不能放皇后,0的位置可以
// rightDiaLim 右斜线的限制,1的位置不能放皇后,0的位置可以
func processQueen2(limit, colLim, leftDiaLim, rightDiaLim int32) int32  { //复杂度 O(N^N)  只是加速了常数时间
	if colLim == limit { // base case
		return 1
	}
	// 所有候选皇后的为孩子,都在pos上
	var pos          int32 = limit & (^(colLim | leftDiaLim | rightDiaLim))
	var mostRightOne int32 = 0
	var res          int32 = 0
	for pos != 0 {
		mostRightOne = pos & (^pos + 1)  // 提取出最右侧的1,剩下的位置都是0
		pos = pos - mostRightOne   // pos ^= mostRightOne
		res += processQueen2(
			limit,
			colLim | mostRightOne,
			(leftDiaLim | mostRightOne) << 1,
			int32(uint32(rightDiaLim | mostRightOne) >> 1), //此处应该无符号右移
			)
	}
	return res
}

/*
8皇后问题为例
                   -
 列limit   0.....0 | 00 00 00 00
 左limit   0.....0 | 00 00 00 00
 右limit   0.....0 | 00 00 00 00
                  -

 0行,左,右都没有限制
 列limit   0.....0 | 10 00 00 00
 左limit   0.....0 | 00 00 00 00
 右limit   0.....0 | 00 00 00 00

 1行,左右对角线限制
 列limit   0.....0 | 10 00 00 00
 左limit   0.....1 | 00 00 00 00   左移1位,左对角线不能放皇后的位置
 右limit   0.....0 | 01 00 00 00   右移1位,右对角线不能放皇后的位置

 限制会下传
 用或计算总限制
 总限制    0.....1 | 11 00 00 00
 */
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

metabit

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值