《算法通关之路》-chapter3游戏之乐

《算法通关之路》学习笔记,记录一下自己的刷题过程,详细的内容请大家购买作者的书籍查阅。

外观数列

力扣第38题
给定一个正整数 n ,输出外观数列的第 n 项。
「外观数列」是一个整数序列,从数字 1 开始,序列中的每一项都是对前一项的描述。

'''
方法一:迭代法
时间复杂度:O(mn)
'''
class Solution:
    def countAndSay(self, n: int) -> str:
        s = '1'
        for _ in range(n-1):
            tmp = ''
            left, right = 0, 0 # 具有相同数字的区间 
            while right < len(s):
                while right < len(s) and s[right] == s[left]: # 区间增长
                    right += 1
                tmp += str(len(s[left: right])) + s[left] # 记录区间长度和对应数字
                left = right
            s = tmp
        return s

# 试运行
n = 4
solu = Solution()
solu.countAndSay(n)
'''
方法二:递归
时间复杂度:O(mn)
'''
class Solution:
    def countAndSay(self, n: int) -> str:
        # 因为tmp每次都要置空,而s每次使用上一轮迭代结果,所以可以使用递归
        def helper(n: int) -> str:
            if n == 1:
                return '1'
            s = helper(n-1)
            tmp = ''
            left, right = 0, 0 # 具有相同数字的区间 
            while right < len(s):
                while right < len(s) and s[right] == s[left]: # 区间增长
                    right += 1
                tmp += str(len(s[left: right])) + s[left] # 记录区间长度和对应数字
                left = right
            return tmp
        
        return helper(n)

# 试运行
n = 4
solu = Solution()
solu.countAndSay(n)

24点

力扣第679题
给定一个长度为4的整数数组 cards 。你有 4 张卡片,每张卡片上都包含一个范围在 [1,9] 的数字。您应该使用运算符 [‘+’, ‘-’, ‘*’, ‘/’] 和括号 ‘(’ 和 ‘)’ 将这些卡片上的数字排列成数学表达式,以获得值24。
如果可以得到这样的表达式,其计算结果为 24 ,则返回 true ,否则返回 false 。

'''
方法一:全排列(回溯递归法)
'''
class Solution:
    def judgePoint24(self, cards: list[int]) -> bool:
        n = len(cards)
        cards.sort()
        used = [False] * n # 构造全排列时用到的访问数组
        tmp = list() # 存全排列数

        def dfs() -> bool:
            '''构造全排列序列,并当即进行判断'''
            if len(tmp) == len(cards):
                return compute(tmp) # 是否组成24点
            for i in range(n):
                if i > 0 and cards[i] == cards[i-1] and used[i-1] == 0: # 去重
                    continue
                if used[i]:
                    continue
                else:
                    tmp.append(cards[i])
                    used[i] = True # 选择card[i]
                    if dfs():
                        return True
                    tmp.pop() # 不选择card[i]
                    used[i] = False
            return False

        def compute(cards: list[int]) -> bool:
            '''判断四个数字能否组成24'''
            if len(cards) == 1:
                return abs(cards[0] - 24) < 1e-5 # 满足题目中的实数除法
            for i in range(0, len(cards)-1):
                if cards[i+1] == 0:
                    compute_res = [cards[i] * cards[i+1], cards[i] + cards[i+1], cards[i] - cards[i+1]]
                else:
                    compute_res = [cards[i] * cards[i+1], cards[i] / cards[i+1], cards[i] + cards[i+1], cards[i] - cards[i+1]]
                for c_res in compute_res:
                    new_cards = cards[:]
                    new_cards[i] = c_res
                    new_cards.pop(i + 1)
                    if compute(new_cards):
                        return True
            return False
        
        return dfs()

# 试运行
cards = [4, 1, 8, 7]
solu = Solution()
solu.judgePoint24(cards)
'''
方法二:组合(迭代递归法)
'''
class Solution:
    def judgePoint24(self, cards: list[int]) -> bool:

        def compute(nums: list[int], n: int) -> bool:
            if n == 1:
                return abs(nums[0] - 24) < 1e-6
            
            new_nums = [0] * 4
            # 双指针计算组合
            for left in range(n-1):
                for right in range(left + 1, n):
                    idx = 0
                    # 将未参与计算的数字移动到数组的前面
                    for i in range(n):
                        if i != left and i != right:
                            new_nums[idx] = nums[i]
                            idx += 1
                    # 将运算后的结果附在其他数字末尾
                    if nums[left] and nums[right]:
                        compute_res = [nums[left] + nums[right], nums[left] * nums[right], nums[left] / nums[right],\
                            nums[right] / nums[left], nums[right] - nums[left], nums[left] - nums[right]]
                    else:
                        compute_res = [nums[left] + nums[right], nums[left] * nums[right], \
                            nums[left] - nums[right], nums[right] - nums[left]]
                    for c_res in compute_res:
                        new_nums[idx] = c_res
                        if compute(new_nums, idx + 1):
                            return True
            return False
        
        return compute(cards, 4)

# 试运行
cards = [4, 1, 8, 7]
solu = Solution()
solu.judgePoint24(cards)

解数独

力扣第37题
编写一个程序,通过填充空格来解决数独问题。
数独的解法需遵循如下规则:
数字 1-9 在每一行只能出现一次。
数字 1-9 在每一列只能出现一次。
数字 1-9 在每一个以粗实线分隔的 3x3 宫内只能出现一次。(请参考示例图)
数独部分空格内已填入了数字,空白格用 ‘.’ 表示。

'''
方法一:回溯
'''
from pprint import pprint
class Solution:
    def solveSudoku(self, board: list[list[str]]) -> None:
        """
        Do not return anything, modify board in-place instead.
        """
        row_state = [[False for i in range(10)] for _ in range(9)] # 行状态 
        column_state = [[False for i in range(10)] for _ in range(9)] # 列状态
        box_state = [[False for i in range(10)] for _ in range(9)] # 九宫格状态

        # 初始化row_state, column_state, box_state三个状态数组
        for i in range(9):
            for j in range(9):
                if board[i][j] != '.':
                    num = int(board[i][j])
                    row_state[i][num] = True # i行的num已经使用过
                    column_state[j][num] = True # j列的num已经使用过
                    box_state[(i // 3) * 3 + j // 3][num] = True # 第[(i // 3) * 3 + j // 3]个九宫格中的num已经使用过
        
        def recursive_place_number(row: int, column: int) -> bool:
            if column == 9: # 一行已经走完
                row += 1
                column = 0
                if row == 9: # 所有行都已走完
                    return True

            if board[row][column] != '.':
                return recursive_place_number(row, column + 1)
            else:
                for i in range(1, 10):
                    if row_state[row][i] or column_state[column][i] or box_state[(row // 3) * 3 + column // 3][i]:
                        continue
                    else:
                        place_number(row, column, i) # 填数并修改三个数组的状态
                        if recursive_place_number(row, column + 1):
                            return True
                        undo_number_placement(row, column, i) # 删数并修改三个数组的状态
            return False

        def place_number(row: int, column: int, i: int) -> bool:
            row_state[row][i] = True
            column_state[column][i] = True
            box_state[(row // 3) * 3 + column // 3][i] = True
            board[row][column] = str(i)
    
        def undo_number_placement(row:int, column:int, i:int) -> bool:
            row_state[row][i] = False
            column_state[column][i] = False
            box_state[(row // 3) * 3 + column // 3][i] = False
            board[row][column] = '.'
        
        recursive_place_number(0, 0)

# 试运行
board = [["5","3",".",".","7",".",".",".","."],
         ["6",".",".","1","9","5",".",".","."],
         [".","9","8",".",".",".",".","6","."],
         ["8",".",".",".","6",".",".",".","3"],
         ["4",".",".","8",".","3",".",".","1"],
         ["7",".",".",".","2",".",".",".","6"],
         [".","6",".",".",".",".","2","8","."],
         [".",".",".","4","1","9",".",".","5"],
         [".",".",".",".","8",".",".","7","9"]]
solu = Solution()
solu.solveSudoku(board)
pprint(board)
'''
方法一:回溯优化
'''
from pprint import pprint
class Solution:
    def solveSudoku(self, board: list[list[str]]) -> None:
        """
        Do not return anything, modify board in-place instead.
        """
        row_state = [[False for i in range(10)] for _ in range(9)] # 行状态 
        column_state = [[False for i in range(10)] for _ in range(9)] # 列状态
        box_state = [[False for i in range(10)] for _ in range(9)] # 九宫格状态

        # 初始化row_state, column_state, box_state三个状态数组
        for i in range(9):
            for j in range(9):
                if board[i][j] != '.':
                    num = int(board[i][j])
                    row_state[i][num] = True # i行的num已经使用过
                    column_state[j][num] = True # j列的num已经使用过
                    box_state[(i // 3) * 3 + j // 3][num] = True # 第[(i // 3) * 3 + j // 3]个九宫格中的num已经使用过
        
        def recursive_place_number(row: int, column: int) -> bool:
            if column == -1 and row == -1: # 所有空格已填完
                return True
            if board[row][column] != '.': # 非空格不填
                return False
            else:
                for i in range(1, 10):
                    if row_state[row][i] or column_state[column][i] or box_state[(row // 3) * 3 + column // 3][i]:
                        continue
                    else:
                        place_number(row, column, i)
                        x, y = get_max_possible_coordinate()
                        if recursive_place_number(x, y):
                            return True
                        undo_number_placement(row, column, i)
            return False

        def place_number(row: int, column: int, i: int) -> bool:
            row_state[row][i] = True
            column_state[column][i] = True
            box_state[(row // 3) * 3 + column // 3][i] = True
            board[row][column] = str(i)
    
        def undo_number_placement(row:int, column:int, i:int) -> bool:
            row_state[row][i] = False
            column_state[column][i] = False
            box_state[(row // 3) * 3 + column // 3][i] = False
            board[row][column] = '.'
        
        # 选取确定性最大的空格返回坐标
        def get_max_possible_coordinate() -> (int, int):
            x, y, min_count = -1, -1, 9
            for i in range(9):
                for j in range(9):
                    if board[i][j] != '.':
                        continue
                    tmp_count = 9
                    for k in range(9):
                        if row_state[i][k] or column_state[j][k] or box_state[(i // 3) * 3 + j // 3][k]:
                            tmp_count -= 1
                    if tmp_count == 1:
                        return i, j
                    if min_count > tmp_count:
                        min_count = tmp_count
                        x = i
                        y = j
            return x, y
        
        x, y = get_max_possible_coordinate()
        recursive_place_number(x, y)

# 试运行
board = [["5","3",".",".","7",".",".",".","."],
         ["6",".",".","1","9","5",".",".","."],
         [".","9","8",".",".",".",".","6","."],
         ["8",".",".",".","6",".",".",".","3"],
         ["4",".",".","8",".","3",".",".","1"],
         ["7",".",".",".","2",".",".",".","6"],
         [".","6",".",".",".",".","2","8","."],
         [".",".",".","4","1","9",".",".","5"],
         [".",".",".",".","8",".",".","7","9"]]
solu = Solution()
solu.solveSudoku(board)
pprint(board)

生命游戏

力扣第289题
根据 百度百科 , 生命游戏 ,简称为 生命 ,是英国数学家约翰·何顿·康威在 1970 年发明的细胞自动机。
给定一个包含 m × n 个格子的面板,每一个格子都可以看成是一个细胞。每个细胞都具有一个初始状态: 1 即为 活细胞 (live),或 0 即为 死细胞 (dead)。每个细胞与其八个相邻位置(水平,垂直,对角线)的细胞都遵循以下四条生存定律:
如果活细胞周围八个位置的活细胞数少于两个,则该位置活细胞死亡;
如果活细胞周围八个位置有两个或三个活细胞,则该位置活细胞仍然存活;
如果活细胞周围八个位置有超过三个活细胞,则该位置活细胞死亡;
如果死细胞周围正好有三个活细胞,则该位置死细胞复活;
下一个状态是通过将上述规则同时应用于当前状态下的每个细胞所形成的,其中细胞的出生和死亡是同时发生的。给你 m x n 网格面板 board 的当前状态,返回下一个状态。

from pprint import pprint
class Solution:
    def gameOfLife(self, board: list[list[int]]) -> None:
        """
        Do not return anything, modify board in-place instead.
        """
        def get_neighbor_count(i, j, board):
            
            # 邻居坐标边界
            top = max(0, i - 1)
            bottom = min(len(board) - 1, i + 1)
            left = max(0, j - 1)
            right = min(len(board[0]) - 1, j + 1)

            count = 0
            for x in range(top, bottom + 1):
                for y in range(left, right + 1):
                    if board[x][y] == 1 or board[x][y] == -1:
                        count += 1
            return count
        
        # 标记将要死亡或者复活的格子
        for i in range(len(board)):
            for j in range(len(board[0])):
                res = get_neighbor_count(i, j, board)
                if board[i][j] == 1 and res in [3, 4]:
                    board[i][j] = 1
                elif board[i][j] == 1:
                    board[i][j] = -1
                elif board[i][j] == 0 and res == 3:
                    board[i][j] = -2
        
        # 更新格子
        for i in range(len(board)):
            for j in range(len(board[0])):
                if board[i][j] == -2:
                    board[i][j] = 1
                elif board[i][j] == -1:
                    board[i][j] = 0

# 试运行
board = [[0,1,0],[0,0,1],[1,1,1],[0,0,0]]
solu = Solution()
solu.gameOfLife(board)
pprint(board)

github笔记本

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

Jiawen9

你的鼓励将是我创作的最大动力

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

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

打赏作者

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

抵扣说明:

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

余额充值