经典算法:N皇后问题,GO语言实现

八皇后问题,是一个古老而著名的问题,是回溯算法的典型案例。该问题是国际西洋棋棋手马克斯·贝瑟尔于1848年提出:在8×8格的国际象棋上摆放八个皇后,使其不能互相攻击,即任意两个皇后都不能处于同一行、同一列或同一斜线上,问有多少种摆法。

N皇后问题是由8皇后拓展而来,其本质还是8皇后问题,主要思路是通过不断递归摆放棋子来减少可以摆放棋子的宫格,最终找到解。
先从简单的 4*4 宫格来讨论:

初始化 4*4 棋盘,0表示没有棋子,1表示放置了棋子,小于0表示不能放置棋子(路障)
0 0 0 0
0 0 0 0
0 0 0 0
0 0 0 0
从第一行第一个开始遍历,放置一个棋子然后在横竖斜对角放置路障
1 -1 -1 -1
-1 -1 0 0
-1 0 -1 0
-1 0 0 -1
接着递归进入第二行,发现有为0的的宫格表示可以放置棋子,继续放置棋子
1 -1 -1 -1
-1 -1 1 -1
-1 -1 -1 -1
-1 0 -1 -1
此时继续递归进入第三行,发现已经不能放置棋子了,接着返回第二行重新放置棋子。
1 -1 -1 -1
-1 -1 -1 1
-1 0 -1 -1
-1 -1 0 -1
此时继续递归进入第三行放置棋子,直到最后一行可以放置棋子则表示这种布局有解。
实现细节
1.因为每次都是按行向下递归,所以不用在棋子所在行和上方放置路障
2.如果想恢复上一次递归的状态不能直接设置值,例如:
1 -1 -1
-1 -1 1
-1 -1 -1
此时已经递归到第二行,第三行不可放置棋子如果恢复到第二行放置之前的状态,将第二行的棋子和路障去掉会变成:
1 -1 -1
-1 -1 0
-1 0 0
这时我们同时把第一行棋子的路障去掉了,所以不正确,解决办法是用增量而不是固定值,每设置一次棋子都+1,路障对应的位置都-1:
1 -1 -1
-1 -1 1
-1 -1 -2
此时还原的时候棋子-1,路障+1即可:
1 -1 -1
-1 -1 0
-1 0 -1
GO语言实现

package main

import (
	"fmt"
)

const N int = 8

var Board [N][N]int = [N][N]int{{}} //初始化棋盘
var Result int = 0

func setBlock(row int, col int, val int) {
	//设置不可放置宫格
	for r, c := row, col; r < N && c >= 0; {
		//设置左下宫格
		Board[r][c] += val
		c--
		r++
	}
	for r := row; r < N; r++ {
		//设置正下方宫格
		Board[r][col] += val
	}
	for r, c := row, col; r < N && c < N; {
		//设置右下方宫格
		Board[r][c] += val
		c++
		r++
	}
}

func recursive(row int, col int) {
	if row == N {
		Result++
		return
	}
	for ; col < N; col++ {
		if Board[row][col] == 0 {
			//设置不可访问宫格
			setBlock(row, col, -1)
			recursive(row+1, 0)
			//还原上一次设置
			setBlock(row, col, 1)

		}
	}
}

func main() {
	recursive(0, 0)
	fmt.Println(Result)
}
N解的个数
11
20
30
42
510
64
740
892
9352
10724
112680
1214200
1373712
14365596
152279184
1614772512
1795815104
18666090624
194968057848
2039029188884
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值