蓝桥杯丨回溯法

做你想做的,错的算我的

这里是Want595,欢迎光临我的世界 ~

目录

前言 

一、N皇后 

二、数独

三、排列组合

四、子集和

总结 

文章目录


前言 

一些关于回溯法的笔记。

一、N皇后 

问题描述

在N*N的方格棋盘放置了N个皇后,使得它们不相互攻击(即任意2个皇后不允许处在同一排,同一列,也不允许处在与棋盘边框成45角的斜线上)。 你的任务是,对于给定的N(N是正整数且6<=N<15),求出有多少种合法的放置方法。并输出前4个解。比如当N=6时,检查一个如下的6 x 6的棋盘,有六个皇后被放置在棋盘上,使得每行、每列有且只有一个,每条对角线(包括两条主对角线的所有平行线)上至多有一个皇后。

程序设计

#N皇后
def NQueens(n):
    def checkBoard(rowIndex):
        for i in range(rowIndex):
            if cols[i]==cols[rowIndex]:
                return False
            if abs(cols[i]-cols[rowIndex])==rowIndex-i:
                return False
        return True
    def helper(rowIndex):
        if rowIndex==n:
            board=[[0 for i in range(n)] for j in range(n)]
            for i in range(n):
                board[i][cols[i]]=1
            res.append(board)
            return
        for i in range(n):
            cols[rowIndex]=i
            if checkBoard(rowIndex):
                helper(rowIndex+1)
    cols=[0 for i in range(n)]
    res=[]
    helper(0)
    return res
lst=NQueens(4)
for i in lst:
    for j in i:
        print(j)
    print()

具体分析

在NQueens()函数中定义两个子函数,checkBoard()用于判断当前放置的皇后位置是否合法,helper()是我们的递归实现的主要函数。

checkBoard()函数以及helper()函数的参数都是当前行,例如当rowIndex=0时代表当前处在第一行,我们需要在第一行选择一个位置放置一个皇后,源码中的cols存放了皇后的列坐标,cols[0]的值为第一行皇后的列坐标,cols[1]为第二行皇后的列坐标,依次类推,例如cols=[0,1,2,3]就表示当前皇后的坐标为第一行第一列,第二行第二列,第三行第三列以及第四行第四列。所以代码中的cols[rowIndex]=i表示当前第rowIndex行的列坐标为i,然后判断该位置是否合法,如果合法就让行数加一继续判断,如果不合法就让列数加一继续判断,当当前行数等于总行数时就可以结束判断,将当前的满足条件的列表放入结果列表res中,最后输出结果列表即可。

二、数独

问题描述

用回溯法填满一张数独表

解题思路

① 从第一个空格开始。依次尝试1到9的数字,如果数字与盘面冲突就换成下一个数字,如果不冲突就去往第二个空格

② 在第二个空格,同样依次尝试1到9的数字,如果与盘面冲突就换成下一个数字,如果不冲突就去往第三个空格

③ 如果当前空格1到9都填不了,就返回到上一个空格,再依次尝试没有试过的数字,如果与盘面冲突就换成下一个数字

④ 当最后一个空格被填上数字时,就可以将答案加入结果列表

程序设计

#数独
def sudoku(board):
    def checkBoard(row,col,num):
        for i in range(9):
            if i!=col and board[row][i]==num:
                return False
            if i!=row and board[i][col]==num:
                return False
        for i in range(row-row%3,3+row-row%3):
            for j in range(col-col%3,3+col-col%3):
                if i!=row and j!=col and board[i][j]==num:
                    return False
        return True
    def helper(index):
        if index==81:
            solution=copy.deepcopy(board)
            res.append(solution)
            return
        i=index//9
        j=index%9
        if board[i][j]==0:
            for num in range(1,10):
                board[i][j]=num
                if checkBoard(i,j,num):
                    helper(index+1)
                board[i][j]=0
        else:
            helper(index+1)
    res=[]
    helper(0)
    return res
board=[[5,3,0,0,7,0,0,0,0],
       [6,0,0,1,9,5,0,0,0],
       [0,9,8,0,0,0,0,6,0],
       [8,0,0,0,6,0,0,0,3],
       [4,0,0,8,0,3,0,0,1],
       [7,0,0,0,2,0,0,0,6],
       [0,6,0,0,0,0,2,8,0],
       [0,0,0,4,1,9,0,0,5],
       [0,0,0,0,8,0,0,7,9]]
lst=sudoku(board)
for i in lst:
    for j in i:
        print(j)
    print

具体分析

与N皇后问题相似,数独问题主要是对每个空格进行判断,碰壁即回溯。

这个程序是一个基于回溯算法的数独求解器。首先定义了一个checkBoard函数,用来检查当前格子中填入的数字是否合法。然后定义helper函数,用来递归搜索数独的解。在每个格子中填入数字,然后判断该数字是否合法,如果合法则递归搜索下一个格子,如果不合法则回溯到上一个格子重新填数字。最后将所有解存放在一个列表中返回。

该程序的时间复杂度为O(9^m),其中m为数独中空格的数量,因为在每个空格中都要尝试填入1到9的数字,所以时间复杂度为9的m次方。但是由于数独的整体复杂度相对较低,所以该程序的实际运行时间非常快。

三、排列组合

排列

输出列表的全排列

#排列
def permute(nums,solution=[]):
    if not nums:
        res.append(solution)
        return
    for i in range(len(nums)):
        newSolution=solution+[nums[i]]
        newList=nums[0:i]+nums[i+1:]
        permute(newList,newSolution)
res=[]
n=int(input())
lst=[i+1 for i in range(n)]
permute(lst)
for i in res:
    for j in i:
        print(j,end=' ')
    print()

定义一个permute()函数,每次传入两个参数,一个是当前列表,另一个是当前排列后列表,例如lst=[1,2,3,4]时,我们调用该函数,newSolution=[1]作为当前排列的列表,newList1=[2,3,4]作为当前列表,然后开始递归,newSolution变为了[1,2],newList变为[3,4],继续直到newSolution=[1,2,3,4]时此时nums=[],我们将solution也就是上一层的newSolution加入结果列表,然后返回上一层,并且此时newSolution=[1,2,4],newList=[3],再一直递归……

组合

输出列表的所有组合

#组合
def combination(nums,solution,n):
    if n==0:
        res.append(solution)
        return
    for i in range(len(nums)):
        newList=nums[i+1:]
        newSolution=solution+[nums[i]]
        combination(newList,newSolution,n-1)
res=[]
n=int(input())
lst=[i+1 for i in range(n)]
combination(lst,[],2)
for i in res:
    for j in i:
        print(j,end=' ')
    print()

与列表的排列类似,求组合的递归也比较好理解,唯一不同的是newList保存的当前剩余列表变化了,因为组合不能重复,所以我们只需要保存i之后的元素即可,其他基本不变,多了一个参数n用来取nums中的n个数进行组合。 

四、子集和

问题一

给定一个无重复元素的数组nums和一个目标数sum,输出所有使数字和为目标数的组合

#问题一
def combinationSum(nums,sums):
    def helper(curSum,solution,index):
        if curSum>sums:
            return
        if curSum==sums:
            res.append(solution)
            return
        for i in range(index,n):
            newSum=curSum+nums[i]
            newSolution=solution+[nums[i]]
            helper(newSum,newSolution,i)
    n=len(nums)
    nums.sort()
    res=[]
    helper(0,[],0)
    return res
nums=list(map(int,input().split(' ')))
sums=int(input())
lst=combinationSum(nums,sums)
for i in lst:
    for j in i:
        print(j,end=' ')
    print()

这段程序实现了“组合总和”问题,即给定一个无重复元素的数组和一个目标数,找出所有可以使数字和等于目标数的组合。

程序的实现思路是采用回溯算法,首先将数组进行排序,然后从第一个元素开始递归地将当前元素加入组合中,并向下遍历数组,直到找到子集和等于目标数或者当前子集和已经大于目标数。如果子集和等于目标数,则将当前组合加入结果列表中,如果子集和大于目标数,则回溯到上一个元素并继续遍历。

程序的输入分为两部分,首先输入一个整数数组并用空格隔开,然后输入一个整数表示目标数。最后输出所有满足条件的组合。

需要注意的是,程序中的helper函数中的solution参数是一个列表,最终结果也是一个列表的列表。每个子列表表示一个符合要求的组合。

问题二

给定一个集合nums和一个目标数sum,输出所有使子集和为目标数的组合

#问题二
def combinationSum(nums,sums):
    def helper(curSum,solution,index):
        if curSum>sums:
            return
        if curSum==sums:
            res.append(solution)
            return
        for i in range(index,len(nums)):
            newSum=curSum+nums[i]
            newSolution=solution+[nums[i]]
            helper(newSum,newSolution,i+1)
    nums.sort()
    res=[]
    helper(0,[],0)
    return res
nums=list(map(int,input().split(' ')))
sums=int(input())
lst=combinationSum(nums,sums)
for i in lst:
    for j in i:
        print(j,end=' ')
    print()

问题一和问题二的唯一区别就是是否可取重复元素,只要稍微改变一下递归条件即可。 

总结 

本章主要通过回溯法解决一些实际问题。

文章目录

序号文章目录直达链接
1简单排序https://want595.blog.csdn.net/article/details/128478423
2高级排序https://want595.blog.csdn.net/article/details/128498290
3查找算法https://want595.blog.csdn.net/article/details/128518434
4哈希算法https://want595.blog.csdn.net/article/details/128642705
5二叉树https://want595.blog.csdn.net/article/details/128546386
6单链表https://want595.blog.csdn.net/article/details/128549471
7广度优先搜索https://want595.blog.csdn.net/article/details/128562776
8深度优先搜索https://want595.blog.csdn.net/article/details/128688457
9回溯法https://want595.blog.csdn.net/article/details/128620465
10分治算法https://want595.blog.csdn.net/article/details/128791210
11动态规划https://want595.blog.csdn.net/article/details/128788989

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

Want595

感谢小伙伴的支持!

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

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

打赏作者

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

抵扣说明:

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

余额充值