Leetcode-剪枝
51. N皇后 https://leetcode-cn.com/problems/n-queens/
n 皇后问题研究的是如何将 n 个皇后放置在 n×n 的棋盘上,并且使皇后彼此之间不能相互攻击。
给定一个整数 n,返回所有不同的 n 皇后问题的解决方案。
每一种解法包含一个明确的 n 皇后问题的棋子放置方案,该方案中 'Q' 和 '.' 分别代表了皇后和空位。
解:
dfs + 剪枝,枚举每个行。注意在做回溯之前,要把当前放置的皇后拿掉,把其造成影响的标识位都消除。
class Solution:
def solveNQueens(self, n: int) -> List[List[str]]:
def could_place(row, col):
# row这一行是没有放置过的行,要检查col这一列、(row,col)所占两条对角线有没有被放置过,如果都没有,(row,col)可以放皇后
return not (cols[col]+hill_diagonals[row-col]+\
dale_diagonals[row+col]) def place_queen(row, col):
queens.add((row, col)) # 放皇后,记录位置,标记列和两对角线
cols[col] = 1
hill_diagonals[row-col] = 1
dale_diagonals[row+col] = 1 def remove_queen(row, col):
queens.remove((row, col)) # 移除皇后,清空列和两对角线的标记
cols[col] = 0
hill_diagonals[row-col] = 0
dale_diagonals[row+col] = 0 def add_solution():
# 如果找到一个解,按要求记录下来
solution = []
for _, col in sorted(queens):
solution.append('.'*col + 'Q' + '.'*(n-col-1))
output.append(solution) def dfs(row):
# 从第一行row=0开始放置皇后,放到n-1行
for col in range(n): # 对于确定的row,遍历所有列col
if could_place(row, col):
place_queen(row, col) # 如果(row, col)可以放皇后,就放
if row == n-1: # 如果已经放了最后一个,说明找到一个解
add_solution()
else: # 没有放到最后一个的话
dfs(row+1) # 去找row行之后所有可能的放置解法
remove_queen(row, col) # 不管是哪种情况都要回溯,移除当前皇后,进入(row, col+1) 的情况 cols = [0] * n
hill_diagonals = [0] * (2 * n -1)
dale_diagonals = [0] * (2 * n -1)
queens = set()
output = [] dfs(0)
return output
52. N皇后ii https://leetcode-cn.com/problems/n-queens-ii/
给定一个整数 n,返回 n 皇后不同的解决方案的数量。
解:
跟#51基本相同,修改一下最后返回值的处理即可
class Solution:
def totalNQueens(self, n: int) -> int:
def could_place(row, col):
# row这一行是没有放置过的行,要检查col这一列、(row,col)所占两条对角线有没有被放置过,如果都没有,(row,col)可以放皇后
return not (cols[col]+hill_diagonals[row-col]+\
dale_diagonals[row+col]) def place_queen(row, col):
queens.add((row, col)) # 放皇后,记录位置,标记列和两对角线
cols[col] = 1
hill_diagonals[row-col] = 1
dale_diagonals[row+col] = 1 def remove_queen(row, col):
queens.remove((row, col)) # 移除皇后,清空列和两对角线的标记
cols[col] = 0
hill_diagonals[row-col] = 0
dale_diagonals[row+col] = 0 def dfs(row):
nonlocal res
for col in range(n):
if could_place(row, col):
place_queen(row, col)
if row == n-1:
res += 1
else:
dfs(row+1)
remove_queen(row, col) # 把刚才放置的拿掉才能回溯 cols = [0] * n
hill_diagonals = [0] * (2 * n - 1)
dale_diagonals = [0] * (2 * n - 1)
queens = set()
res = 0
dfs(0)
return res
36. 有效的数独 https://leetcode-cn.com/problems/valid-sudoku/
判断一个 9x9 的数独是否有效。只需要根据以下规则,验证已经填入的数字是否有效即可。
数字 1-9 在每一行只能出现一次。
数字 1-9 在每一列只能出现一次。
数字 1-9 在每一个以粗实线分隔的 3x3 宫内只能出现一次。
数独部分空格内已填入了数字,空白格用 '.' 表示。
说明:
一个有效的数独(部分已被填充)不一定是可解的。
只需要根据以上规则,验证已经填入的数字是否有效即可。
给定数独序列只包含数字 1-9 和字符 '.' 。
给定数独永远是 9x9 形式的。
解:
两层嵌套循环遍历即可,分别对行、列、子数独用 n=9 个哈希表(其中key为1~9)来存所有已经遇到过的值,如果某个key的value大于1的话说明数独无效。
class Solution:
def isValidSudoku(self, board: List[List[str]]) -> bool:
# 9行,9列,9个子数独
rows = [{} for _ in range(9)] # 每个哈希表存,列表下标对应行的,已填数字情况
cols = [{} for _ in range(9)]
boxes = [{} for _ in range(9)] # 子数独编号为boxes的下标,从上到下从左到右索引 for i in range(9):
for j in range(9):
num = board[i][j]
if num != '.': # 如果某个位置已经填入数
num = int(num)
box_idx = (i//3)*3 + j//3 # 当前位置所处的子数独索引 rows[i][num] = rows[i].get(num, 0) + 1 # 先保留当前的数,在哈希表中保存
cols[j][num] = cols[j].get(num, 0) + 1
boxes[box_idx][num] = boxes[box_idx].get(num, 0) + 1 # 检查如果保留当前数的话,是否合法
if rows[i][num] > 1 or cols[j][num] > 1 or boxes[box_idx][num] > 1:
return False
return True
37. 解数独 https://leetcode-cn.com/problems/sudoku-solver/
编写一个程序,通过已填充的空格来解决数独问题。
解:
dfs, 枚举每个空格。
from collections import defaultdict
class Solution:
def solveSudoku(self, board: List[List[str]]) -> None:
"""
Do not return anything, modify board in-place instead.
"""
def could_place(d, row, col):
"""
Check if one could place a number d in (row, col) cell
"""
return not (d in rows[row] or d in columns[col] or \
d in boxes[box_index(row, col)]) def place_number(d, row, col):
"""
Place a number d in (row, col) cell
"""
rows[row][d] += 1
columns[col][d] += 1
boxes[box_index(row, col)][d] += 1
board[row][col] = str(d) def remove_number(d, row, col):
"""
Remove a number which didn't lead
to a solution
"""
del rows[row][d]
del columns[col][d]
del boxes[box_index(row, col)][d]
board[row][col] = '.' def place_next_numbers(row, col):
"""
Call backtrack function in recursion
to continue to place numbers
till the moment we have a solution
"""
# if we're in the last cell
# that means we have the solution
if col == N - 1 and row == N - 1:
nonlocal sudoku_solved
sudoku_solved = True
#if not yet
else:
# if we're in the end of the row
# go to the next row
if col == N - 1:
backtrack(row + 1, 0)
# go to the next column
else:
backtrack(row, col + 1) def backtrack(row = 0, col = 0):
"""
Backtracking
"""
# if the cell is empty
if board[row][col] == '.':
# iterate over all numbers from 1 to 9
for d in range(1, 10):
if could_place(d, row, col):
place_number(d, row, col)
place_next_numbers(row, col)
# if sudoku is solved, there is no need to backtrack
# since the single unique solution is promised
if not sudoku_solved:
remove_number(d, row, col)
else:
place_next_numbers(row, col) # box size
n = 3
# row size
N = n * n
# lambda function to compute box index
box_index = lambda row, col: (row // n ) * n + col // n # init rows, columns and boxes
rows = [defaultdict(int) for i in range(N)]
columns = [defaultdict(int) for i in range(N)]
boxes = [defaultdict(int) for i in range(N)]
for i in range(N):
for j in range(N):
if board[i][j] != '.':
d = int(board[i][j])
place_number(d, i, j) sudoku_solved = False
backtrack()
除了普通的dfs,还可以加一些剪枝的条件来加速。先枚举可选项少的空格:预处理,先遍历一边格子,找出每个空格的可选数,并排序,dfs搜索时就从可选数最少的空格开始。
Leetcode-剪枝的更多相关文章
- LeetCode:二叉树剪枝【814】
LeetCode:二叉树剪枝[814] 题目描述 给定二叉树根结点 root ,此外树的每个结点的值要么是 0,要么是 1. 返回移除了所有不包含 1 的子树的原二叉树. ( 节点 X 的子树为 X ...
- Leetcode题目39.组合总和(回溯+剪枝-中等)
题目描述: 给定一个无重复元素的数组 candidates 和一个目标数 target ,找出 candidates 中所有可以使数字和为 target 的组合. candidates 中的数字可以无 ...
- LeetCode专题——详解搜索算法中的搜索策略和剪枝
本文始发于个人公众号:TechFlow,原创不易,求个关注 今天是LeetCode专题第20篇文章,今天讨论的是数字组合问题. 描述 给定一个int类型的候选集,和一个int类型的target,要求返 ...
- leetcode 1593. 拆分字符串使唯一子字符串的数目最大(DFS,剪枝)
题目链接 leetcode 1593. 拆分字符串使唯一子字符串的数目最大 题意: 给你一个字符串 s ,请你拆分该字符串,并返回拆分后唯一子字符串的最大数目. 字符串 s 拆分后可以得到若干 非空子 ...
- 图解Leetcode组合总和系列——回溯(剪枝优化)+动态规划
Leetcode组合总和系列--回溯(剪枝优化)+动态规划 组合总和 I 给定一个无重复元素的数组 candidates 和一个目标数 target ,找出 candidates 中所有可以使数字和为 ...
- Java实现 LeetCode 814 二叉树剪枝 (遍历树)
814. 二叉树剪枝 给定二叉树根结点 root ,此外树的每个结点的值要么是 0,要么是 1. 返回移除了所有不包含 1 的子树的原二叉树. ( 节点 X 的子树为 X 本身,以及所有 X 的后代. ...
- Leetcode 814. 二叉树剪枝
题目链接 https://leetcode-cn.com/problems/binary-tree-pruning/description/ 题目描述 给定二叉树根结点 root ,此外树的每个结点的 ...
- [LeetCode] Find K Pairs with Smallest Sums 找和最小的K对数字
You are given two integer arrays nums1 and nums2 sorted in ascending order and an integer k. Define ...
- [LeetCode] Coin Change 硬币找零
You are given coins of different denominations and a total amount of money amount. Write a function ...
- [LeetCode] Word Ladder II 词语阶梯之二
Given two words (start and end), and a dictionary, find all shortest transformation sequence(s) from ...
随机推荐
- 温故知新——Spring AOP(二)
上一篇我们介绍Spring AOP的注解的配置,也叫做Java Config.今天我们看看比较传统的xml的方式如何配置AOP.整体的场景我们还是用原来的,"我穿上跑鞋",&quo ...
- Javascript常见数据类型API
1 - 内置对象 1.1 内置对象 JavaScript 中的对象分为3种:自定义对象 .内置对象. 浏览器对象 前面两种对象是JS 基础 内容,属于 ECMAScript: 第三个浏览器对象 ...
- 【python】迭代器与生成器到底是什么?看完你就知道
迭代器跟生成器,与上篇文章讲的装饰器一样,都是属于我的一个老大难问题. 通常就是遇到的时候就去搜一下,结果在一大坨各种介绍博客中看了看,回头又忘记了. 你是不是也是这样呢? 俗话说:好记性不如烂笔头, ...
- WPF实现手势解锁
桌面程序的解锁方式一般是账号密码,互联网的可以使用扫码解锁,甚至人脸识别.但扫码需要网络,人脸识别又较复杂.所以就想把安卓常用的手势解锁移植到桌面程序上. 先来张效果图,有兴趣的往下看,没兴趣的打扰了 ...
- Opencv+Yolov3算法实现社交距离安全检测讲解和实战(Social Distance Detector)
在我们进行交流谈话时,人与人之间总要保持一定的距离,尤其是在疫情的情况下,人与人之间更要保持一定的安全距离,今天给大家来介绍一个检测社交距离的项目,实现社交距离检测器. 社交距离(Social Dis ...
- 转:brpc的研发经历
转载自:https://www.jianshu.com/p/124dc2c7d9d3 RPC是个老概念,五花八门的实现非常多.在14年我刚转到基础架构部时,其实是不想做RPC框架的.我的想法可能和很多 ...
- 20190923-10Linux进程线程类 000 018
进程是正在执行的一个程序或命令,每一个进程都是一个运行的实体,都有自己的地址空间,并占用一定的系统资源. ps 查看当前系统进程状态 ps:process status 进程状态 1.基本语法 ps ...
- Oracle序列Sequence用法
序列 序列(Sequence)是用来生成连续的整数数据的对象.序列常常用来作为主键中增长列,序列中的可以升序生成,也可以降序生成.创建序列的语法是:语法结构:创建序列 CREATE SEQUENCE ...
- transition实现图片轮播
<!DOCTYPE html> <html> <head> <meta charset="utf-8" /> <title&g ...
- HTML-CSS-JS Prettify 代码格式化插件
前提:已经安装 node.js.安装插件 HTML-CSS-JS Prettify,修改node路径,即可通过单击右键 HTML-CSS-JS Prettify 中的 Prettify Code 使用 ...