回溯法——n皇后问题、2n皇后题解

1. 回溯算法详解

回溯算法模板

result = []
def backtrack(路径, 选择列表):
    if 满足结束条件:
        result.add(路径)
        return
    for 选择 in 选择列表:
        做选择
        backtrack(路径, 选择列表)
        撤销选择

【注意】python中如果append列表要用深拷贝! Blist.append(Alist.copy())

2. n皇后问题

思路其实很简单,我们其实可以直接采用一个一维数组,来表示棋盘,因为棋盘上一行只能有一个皇后,所以的话,我们这样子表示的话,直接能表示出每一个皇后在第几行第几列,然后我们就一行行来找,第一行放在第几个,第二行放在第几个…,如此直到找到每一行都有一个皇后的解,然后tot++;然后返回,这一行继续往下找,看看是否还有满足的解,主要的是一个判断函数,就是在这一行之前看看是否有皇后与之同列或者对角线即可。

C++代码如下

#include<bits/stdc++.h>
#define N 10 
using namespace std;

int n;//输入n皇后 
int ans[N];//存放每一行皇后摆放的位置,行是隐式的,只用存放列就行,只需一维数组就可以表示!!,这边从ans[1]代表第一行摆放位置
int tot = 0;//用于统计解的个数 

bool check(int ans[],int x,int y){//判断是否冲突 x行y列,有冲突返回0,没冲突返回10
bool flag = 0; int res; for(int i=1;i<=x-1 && !flag;i++){//当前x行,看前面1- - - x-1行, 同时flag主要是避免重复判读,只要有一行有冲突,就不用判断了,直接flag=1 res = ans[i];//res临时存放当前行皇后摆放的列索引 if(res == y || abs(x - i) == abs(y - res))//判断一下,我们当前要放的该行x该列y,与遍历的行i列res,是否同列 res==y? 是否在对角线上?注意对角线的判断条件写法! 举个特例即可理解 1,1 3,3 两个坐标是在对角线上,满足该公式 flag = 1; } return !flag; } void dfs(int m){//回溯+深度优先搜索 if(m == n+1){//一共n行,全部放完n行,记录一次结果 tot++; return ; } for(int j=1;j<=n;j++){//遍历n列 if(check(ans,m,j)){//如果不冲突 看当前防止位置m,j是否冲突
ans[m] = j;//放置并记录 dfs(m+1);//进入下一行放置皇后 } } return ; } int main(){ cin>>n; dfs(1);//从第一行开始 cout<<tot<<endl; return 0; }

Python代码如下

 

def check(board, row, col):
    for i in range(row):
        if abs(board[i]-col) == 0 or abs(board[i]-col) == abs(i-row):#不能放在同一列或者对角线上
            return False
    return True
 
 
def dfs(board, row):#board是棋盘选择列表,row是路径
    if row == len(board):
        print(board)
        printBoard(board);
        return;

    for col in range(len(board)):
        if check(board, row, col):
            board[row] = col
            dfs(board, row+1)
       board[row] = 0
def printBoard(board): for i,col in enumerate(board):#注意这里遍历枚举的方法,可以把一维的数组打印出二维 print('' * col + '' + '' * (len(board) - 1 - col)) print("") board = [0 for i in range(4)] #模拟棋盘,这里用一维模拟,因为行是隐式的,只用存储列下标 dfs(board, 0) #从第0行开始放皇后

运行结果如下

 

 

 

3. 2n皇后 -蓝桥杯基础

https://www.dotcpp.com/oj/problem1460.html 连接

这题是八皇后问题的变形、八皇后是放一个皇后、本题2n皇后是放两个皇后。

解题思路:

        我们可以先放好一个皇后后再放另一个皇后。在图里可以放皇后的格子为1,所以我们可以将不同皇后设置不同的数字来代表,比如2代表黑皇后,3代表白皇后。我们每放一个皇后时先检查他所在列,和两边的对角线有没有放皇后或者说是不能放皇后,判断条件是格子的数是否为一,不为一则是放了皇后或者是不能放皇后。放完最后一行后、我们在dfs函数里判断当前放的皇后是否是将所有的皇后放完了,我们可以用一个数字s代表当前放的棋子,判断条件是s是否等于最后要放的棋子,如果是则放完了计数器count加一,否则继续放棋子,从第一行开始,传下一个代表棋子的数字参数。看到这再看代码相信就明白了。

n = int(input())
mapL = [list(map(int,input().split())) for _ in range(n)]   #模拟棋盘,接收二维数组
count = 0   #计数器

def dfs(row,n,s,mapL):#row行 n列 s代表当前放置的皇后是黑or白,maL代表棋盘
    global count
    if row == n:    #判断是否是放完了最后一行,注意我的行数是从0开始,0代表第一行,所以从0---n-1 行数   
        if s == 2:  #2代表黑皇后,3代表白皇后
            dfs(0,n,3,mapL) #2黑皇后放完,开始放白皇后,从0行重新开始
        if s == 3:  #全部放完,统计一次答案
            count += 1
        return
    
    for i in range(n):#从n列中做选择
        if mapL[row][i] != 1:   #不为1、说明放了皇后,或者不能皇后,选择下一列
            continue
        if check(row,i,s,mapL): #当前放置的位置为row,i,要放置的皇后为s,检测是否冲突
            mapL[row][i] = s    #做选择,可以放,将格子的数字变为放置对应皇后的数字
            dfs(row+1,n,s,mapL) #继续到下一行row+1放下一个同色皇后
            mapL[row][i] = 1    #撤销选择
 
def check(row,j,s,mapL):

    for i in range(0,row-1):    #检查对应列,列不动,遍历前面0-row-1所有行
        if mapL[i][j] == s:
            return False
        
    r = row - 1
    k = j - 1    
    while r>=0 and k>=0:    #检查对应左上角r.k 是row-1,j-1左边的左上角的一个坐标
        if mapL[r][k] == s:
            return False
        r -= 1 #继续找左上角一个
        k -= 1
        
    r = row -1 #重新设置初始值,为右上角一个坐标
    k = j + 1
    while r>=0 and k<n: #检查对应右上角,直到右角边界
        if mapL[r][k] == s:
            return False
        r -= 1
        k += 1 #继续找右上角一个
        
    return True
dfs(0,n,2,mapL)
print(count)

注意这里判断函数的写法,与n皇后区分,n皇后只有一种棋子,棋子记1,可以用一个board来记录放置的列,从而写出判断条件;而2n皇后棋子记2 3 无法用一维记录,只能用二维记录,判断条件是一个个找右上角,注意写法!!!先定位到右上角,然后while直到边界,不断的前进。

 

4.拓展 leetcode n皇后问题解决

https://blog.csdn.net/qq_42589613/article/details/110801312

class Solution:
    def solveNQueens(self, n: int) -> List[List[str]]:
        board = [['.'] * n for _ in range(n)]#建立棋盘二维的
        res = []
        def check(board,row, col):
            #判断同一列是否冲突
            for i in range(len(board)):
                if board[i][col] == 'Q':
                    return False
            # 判断左上角是否冲突
            i = row -1
            j = col -1
            while i>=0 and j>=0:
                if board[i][j] == 'Q':
                    return False
                i -= 1
                j -= 1
            # 判断右上角是否冲突
            i = row - 1
            j = col + 1
            while i>=0 and j < len(board):
                if board[i][j] == 'Q':
                    return False
                i -= 1
                j += 1
            return True

        def backtrace(board, row, n):#row 路径 n 选择列表
            # 如果走到最后一行,说明已经找到一个解
            if row == n:
                temp_res = []
                for temp in board:#遍历每一行,['.','Q','.']每行是列表,需要拼接成字符串再装配
                    temp_str = "".join(temp)#把列表每个元素拼接成字符串
                    temp_res.append(temp_str)
                res.append(temp_res)
            for col in range(n):
                if check(board, row, col):
                    board[row][col] = 'Q'
                    backtrace(board, row+1, n)
                    board[row][col] = '.'
            
        backtrace(board, 0, n)
        return res

注意格式问题,还是套回溯的模板

posted @ 2022-01-24 11:34  何梦吉他  阅读(686)  评论(0编辑  收藏  举报