[python刷题模板] DFS回溯枚举

一、 算法&数据结构

1. 描述

dfs枚举是经常会用到的算法,比如小球放盒子,枚举所有组合情况。

2. 复杂度分析

  1. O(xn)
  2. O(n!)
  3. O(C(n,m))

3. 常见应用

  1. 指数型枚举
  2. 排列型枚举
  3. 组合型枚举

4. 常用优化

在这之前,请千万不要忘记我们用的是python!
指数:

class Solution(object):
    def letterCasePermutation(self, S):
        f = lambda x: (x.lower(), x.upper()) if x.isalpha() else x
        # print(list(map(f,S)))
        # print(*map(f,S))
        return list(map("".join, itertools.product(*map(f, S))))

排列:

class Solution:
    def permute(self, nums: List[int]) -> List[List[int]]:       
        return list(itertools.permutations(nums) )

组合:

class Solution:
    def combine(self, n: int, k: int) -> List[List[int]]:        
        return list(itertools.combinations(range(1,n+1),k))
  1. 剪枝

二、 模板代码

1. 三种枚举

#!/usr/bin/env python
# -*- encoding: utf-8 -*-
"""
@File       :   DFS.py    
@License    :   (C)Copyright 2013-2022, capstone
@Project    :   NormalTools
@Software   :   PyCharm
@ModifyTime :   2022/5/31 17:12    
@Author     :   liushuliang
@Version    :   1.0
@Description:   None
"""

def dfs_exp(step, arr, result, mask=True):
    """
    指数型枚举,每个元素都可以存在或不存在
    枚举每个元素
    :param step: 枚举第几个数
    :param arr: 原数组
    :param mask: 剪枝条件,用mask判断这个数是否需要放
    :return:
    """
    if step == len(arr):
        print(result)
        return
    dfs_exp(step + 1, arr, result[:], mask)  # 不选本数,直接处理下一步
    new_mask = mask and True  # 通过mask判断是否选本数
    if new_mask:
        dfs_exp(step + 1, arr, result + [a[step]], new_mask)  # 如果选本数,mask更新


a = [1, 2, 3]
n = len(a)


# dfs_exp(0,a,[],True)

def dfs_permutation(step, arr, result, help, mask=True):
    """
    排列型枚举,每个元素都要存在,枚举所有位置
    枚举每个位置,放每个数,回溯
    :param step: 枚举第几个位置
    :param arr: 原数组
    :param mask: 剪枝条件,用mask判断这个数是否需要放
    :return:
    """
    if step == len(arr):
        print(result)
        return

    for i in range(len(arr)):
        if help[i] == 0:
            help[i] = 1
            dfs_permutation(step + 1, arr, result + [arr[i]], help, mask)
            help[i] = 0


a = [1, 2, 3, 4]
help_arr = [0] * n


# dfs_permutation(0,a,[],help_arr)

def dfs_combination(step, start, arr, limit, result, mask=True):
    """
    组合型枚举,从arr中任选limit个数,顺序不重要
    :param step:
    :param arr:
    :param result:
    :param help:
    :param mask:
    :return:
    """
    if step == limit:
        print(result)
        return
    for i in range(start, len(arr)):
        dfs_combination(step + 1, i + 1, arr, limit, result + [arr[i]])


a = [ 2, 3, 4,1]
dfs_combination(0, 0, a, 3, [])

2.指数型枚举

链接: 473. 火柴拼正方形
链接: [LeetCode解题报告] 473. 火柴拼正方形

class Solution:
    def makesquare(self, matchsticks: List[int]) -> bool:
        n = len(matchsticks)
        if n < 4:
            return False
        s = sum(matchsticks)
        side = s // 4
        if side * 4 != s:
            return False
        matchsticks.sort(reverse=True)
        res = [0] * 4
        # 枚举每个数字是否出现能组合成什么值,2^n;这个组合值用于下边dfs的剪枝:
        # 如果放火柴之后,本边剩余长度不能用火柴组合起来,那直接剪掉
        # 本来我就是试试,没想到剪成功了!
        cmb = set()
        def dfs_sum(i,s):
            if i == n:
                cmb.add(s)
                return 
            dfs_sum(i+1,s)
            dfs_sum(i+1,s+matchsticks[i])
        dfs_sum(0,0)
        # print(cmb)
        def dfs(i):
            if i == n:
                return True
                # return all(res[j]==side for j in range(4))
            
            for j in range(4):
                if  res[j]+matchsticks[i]>  side or side - res[j]-matchsticks[i] not in cmb :                    
                    continue
                res[j] += matchsticks[i]               
                if dfs(i+1):
                    return True
                res[j] -= matchsticks[i]
            return False

        return dfs(0)

784. 字母大小写全排列


class Solution:
    def letterCasePermutation(self, s: str) -> List[str]:
        n = len(s)
        ans = []
        ret = ['']*n
        def dfs(pos):
            if pos ==n :
                return ans.append(''.join(ret))
            c = s[pos]
            if c.isdigit():
                ret[pos] = c 
                dfs(pos + 1)
            else:
                ret[pos] = s[pos].lower()
                dfs(pos+1)
                ret[pos] = s[pos].upper()
                dfs(pos+1)
        dfs(0)
        return ans

3. 排列型枚举

简单点
46. 全排列

class Solution:
    def permute(self, nums: List[int]) -> List[List[int]]:
        n = len(nums)
        used = [0]*n
        ans = []
        def dfs(s):
            if len(s) == n:
               ans.append(s[:])
               return 
            for i in range(n):
                if not used[i]:
                    used[i] = 1
                    dfs(s+nums[i:i+1])
                    used[i] = 0
        dfs([])
        return ans 
    a = [1,2,3,4]
    n = len(a)
    used = [0]*n
    ret = [0]*n
    ans = []

    def dfs(i):
        if i == n:
            ans.append(ret[:])
            # print(ret)

            return
        for j in range(n):
            if used[j] == 0:
                used[j] = 1
                ret[i] = a[j]
                dfs(i+1)
                used[j] = 0

    dfs(0)

4.组合型枚举

77. 组合

class Solution:
    def combine(self, n: int, k: int) -> List[List[int]]:        
        ans = []
        def dfs(start,s):
            if len(s) == k:
                ans.append(s[:])
                return 
            for i in range(start,n):                
                dfs(i+1,s+[i+1])
        
        dfs(0,[])
        return ans 

三、其他

四、更多例题

五、参考链接

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
POJ 1321 排兵布阵问题可以使用 DFS 算法求解。 题目要求在一个 n x n 的棋盘上,放置 k 个棋子,其中每行、每列都最多只能有一个棋子。我们可以使用 DFS 枚举每个棋子的位置,对于每个棋子,尝试将其放置在每一行中未被占用的位置上,直到放置了 k 个棋子。在 DFS 的过程中,需要记录每行和每列是否已经有棋子,以便在尝试放置下一个棋子时进行判断。 以下是基本的 DFS 模板代码: ```python def dfs(row, cnt): global ans if cnt == k: ans += 1 return for i in range(row, n): for j in range(n): if row_used[i] or col_used[j] or board[i][j] == '.': continue row_used[i] = col_used[j] = True dfs(i + 1, cnt + 1) row_used[i] = col_used[j] = False n, k = map(int, input().split()) board = [input() for _ in range(n)] row_used = [False] * n col_used = [False] * n ans = 0 dfs(0, 0) print(ans) ``` 其中,row 代表当前尝试放置棋子的行数,cnt 代表已经放置的棋子数量。row_used 和 col_used 分别表示每行和每列是否已经有棋子,board 则表示棋盘的状态。在尝试放置棋子时,需要排除掉无法放置的位置,即已经有棋子的行和列,以及棋盘上标记为 '.' 的位置。当放置了 k 个棋子时,即可计数一次方案数。注意,在回溯时需要将之前标记为已使用的行和列重新标记为未使用。 需要注意的是,在 Python 中,递归深度的默认限制为 1000,可能无法通过本题。可以通过以下代码来解除限制: ```python import sys sys.setrecursionlimit(100000) ``` 完整代码如下:

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值