笔记:蓝桥杯python搜索(3-2)——DFS剪支和记忆化搜索

news2025/2/12 23:57:36

目录

 一、DFS剪支

二、例题

P2942 数字王国之军训军队

 P3075 特殊的多边形

三、记忆化搜索

四、例题

例题 P3820 混境之地

P216 地宫取宝


 一、DFS剪支

  • 在搜索过程中,如果需要完全遍历所有情况可能需要很多时间
  • 在搜索到某种状态时,根据当前状态判断出后续无解,则该状态无需继续深入搜索
  • 例如:给定N个正整数,求出有多少个子集之和小于等于K。在搜索过程中当前选择的数字和已经超过K则不需要继续搜索。
  • 可行性剪枝:当前状态和题意不符,并且往后的所有情况和题意都不符,那么就可以进行剪枝。
  • 最优性剪枝:在搜索过程中,当前状态已经不如已经找到的最优解,也可以剪枝,不需要继续搜索。

二、例题

P2942 数字王国之军训军队

数字王国开学了,它们也和我们人类一样有开学前的军训,现在一共有 n 名学生,每个学生有自己的一个名字 ai​(数字王国里的名字就是一个正整数,注意学生们可能出现重名的情况),此时叛逆教官来看了之后感觉十分别扭,决定将学生重新分队。

排队规则为:将学生分成若干队,每队里面至少一个学生,且每队里面学生的名字不能出现倍数关系(注意名字相同也算是倍数关系)。

现在请你帮忙算算最少可以分成几队?

例:有 4 名学生 (2,3,4,4),最少可以分成 (2,3)、(4)、(4) 共 3 队。

输入格式

第一行包含一个正整数 n,表示学生数量。

第二行包含 n 个由空格隔开的整数,第 i 个整数表示第 i 个学生的名字 ai​。

输出格式

输出共 1 行,包含一个整数,表示最少可以分成几队。

DFS 搜索,枚举每个学生分到每个组内

可行性剪枝:要满足题目条件

最优性剪枝:判断当前状态是否比 ans 更劣


def check(x,group):
    for y in group:
        if x%y ==0 or y%x==0:
            return False
    return True


def dfs(depth):
    if depth==n:
        global answer
        answer=min(answer,len(Groups))
        return

    for every_group in Groups:
        if check(a[depth],every_group):
            every_group.append(a[depth])
            dfs(depth+1)
            every_group.pop()
    Groups.append([a[depth]])
    dfs(depth+1)
    Groups.pop()

n=int(input())
a=list(map(int,input().split()))
Groups=[]
answer=n
dfs(0)
print(answer)
# 判断x能否加入group组   
def check(x, group):
    # 要保证不能存在倍数关系
    for y in group:
        if x % y == 0 or y % x == 0:
            return False
    return True
   
# depth表示当前为第depth个学生   
def dfs(depth):
    global ans
    # 最优性剪枝:当前已经比ans大,说明该策略不可行
    if len(Groups) > ans:
        return
    if depth == n:
        ans = min(len(Groups), ans)
        return
    for each_group in Groups:
        # 枚举第depth个学生能否加入当前组each_group
        # 剪枝:必须满足题意
        if check(a[depth], each_group):
            each_group.append(a[depth])
            dfs(depth + 1)
            each_group.pop()
    # 单独作为一组,将 ​a[depth]​ 作为一个新的分组添加,即 ​Groups.append([a[depth]])​
    Groups.append([a[depth]])
    dfs(depth + 1)
    Groups.pop()
   
n = int(input())   
a = list(map(int, input().split()))   
# ans表示最少能分多少队   
ans = n
   
# Groups表示分组情况   
Groups = []   
dfs(0)   
print(ans)

 P3075 特殊的多边形

假设一个 n 边形 n 条边为 a1,a2,a3,⋯,an​,定义该 n 边形的值 v=a1×a2×a3×⋯×an。

定义两个 n 边形不同是指至少有一条边的长度在一个 n 边形中有使用而另一个 n 边形没有用到,如 n 边形 (3,4,5,6)和 (3,5,4,6) 是两个相同的 n 边形,(3,4,5,6)和 (4,5,6,7) 是两个不相同的 n 边形。

现在有 t 和 n,表示 t 个询问并且询问的是 n 边形,每个询问给定一个区间 [l,r],问有多少个 n 边形(要求该 n 边形自己的 n 条边的长度互不相同)的值在该区间范围内。

输入格式

第一行包含两个正整数 t、n,表示有 t 个询问,询问的是 n 边形。

接下来 t 行,每行有两个空格隔开的正整数 l、r,表示询问区间 [l,r]。

输出格式

输出共 t 行,第 i行对应第 i 个查询的 n 边形个数。

  • 先考虑简单版:乘积为 v 有多少种 n 边形

  • DFS 处理出所有乘积对应的所有可能

    • 维护一个递增的边长序列(唯一性)

    • 枚举第 i 边的长度,最小最大范围(剪枝)

    • 最终 check 是否满足 N 边形:

      • 最小的 N - 1 条边之和大于第 N 边

  • 预处理+前缀和 O(1)查询答案


import os
import sys

# 请在此输入您的代码
def dfs(depth, last_val, tot, mul):
    """
    :param depth:    第depth条边长
    :param last_val: 上一边长长度
    :param tot:      累计和
    :param mul:      累计乘积
    """
    if depth == n:
        # 前n-1条边之和大于第n条边
        if tot - path[-1] > path[-1]:
            ans[mul] += 1
        return
    for i in range(last_val + 1, 100001):
        # 最优性剪枝, 后续还有n-depth个数字, 每个数字都要>=i
        # 累计乘积要不超过100000: mul * (i ** (n - depth))
        if mul * (i ** (n - depth)) > 100000:
            break
        path.append(i)
        dfs(depth + 1, i, tot + i, mul * i)
        path.pop()


t, n = map(int, input().split())
ans = [0] * 100001
path = []
dfs(0, 0, 0, 1)

for i in range(1, 100001):
    ans[i] += ans[i - 1]
for _ in range(t):
    l, r = map(int, input().split())
    print(ans[r] - ans[l - 1])

三、记忆化搜索

  • 记忆化:通过记录已经遍历过的状态的信息,从而避免对同一状态重复遍历的搜索实现方式。
  • 记忆化=dfs+额外字典
    • 如果先前已经搜索过:直接查字典,返回字典中结果
    • 如果先前没有搜索过:继续搜索,最终将该状态结果记录到字典中
  • 斐波那契数列:设F[0] = 1, F[1] = 1, F[n] = F[n - 1] + F[n - 2],求F[n],结果对1e9 + 7取模。 0 <= n <= 10000
  • 样例输入: 5000
  • 样例输出: 976496506
def f(n):
    if n==0 or n==1:
        return 1
    return (f(n-1)+f(n-2))%(1e9+7)
n=int(input())
print(f(n))
# 直接递归存在大量重复计算

计算 F(5) 时,要先算 F(4) 和 F(3);算 F(4) 又需算 F(3) 和 F(2),这里 F(3) 就被重复计算了。随着 n 增大,像 F(2)F(3) 这类中间结果会被反复多次计算,导致时间复杂度呈指数级增长,效率极低。

  • 每次搜索时将当前状态答案记录到字典中
  • 后续搜索直接返回结果
import sys
sys.setrecursionlimit(100000)

# 记忆化1
dic={0:1,1:1}
def f(n):
    if n in dic.keys():
        return dic[n]
    dic[n]=(f(n-1)+f(n-2))%1000000007
    return dic[n]
n=int(input())
print(f(n))

 

from functools import lru_cache

#记忆化搜索2
@lru_cache(maxsize=None)
from functools import lru_cache

# 记忆化2
@lru_cache(maxsize=None)
def f(n):
    if n==0 or n==1:
        return 1
    return f(n-1)+f(n-2)
n=int(input())
print(f(n))

四、例题

例题 P3820 混境之地

小蓝有一天误入了一个混境之地。

好消息是:他误打误撞拿到了一张地图,并从中获取到以下信息:

  1. 混境之地是一个 n⋅m 大小的矩阵,其中第 i 行第 j 列的的点 hij​ 表示第 i 行第 j 列的高度。

  2. 他现在所在位置的坐标为 (A,B) ,而这个混境之地出口的坐标为 (C,D) ,当站在出口时即表示可以逃离混境之地。

  3. 小蓝有一个喷气背包,使用时,可以原地升高 k 个单位高度。

坏消息是:

  1. 由于小蓝的体力透支,所以只可以往低于当前高度的方向走。

  2. 喷漆背包燃料不足,只可以最后使用一次。

小蓝可以往上下左右四个方向行走,不消耗能量。

小蓝想知道他能否逃离这个混境之地,如果可以逃离这里,输入 Yes ,反之输出 No 。


输入格式

第 1 行输入三个正整数 n,m 和 k , n,m 表示混境之地的大小, k 表示使用一次喷气背包可以升高的高度。

第 2 行输入四个正整数 A,B,C,D ,表示小蓝当前所在位置的坐标,以及混境之地出口的坐标。

第 3 行至第 n+2 行,每行 m 个整数,表示混境之地不同位置的高度。

输出格式

输出数据共一行一个字符串:

  • 若小蓝可以逃离混境之地,则输出 Yes 。

  • 若小蓝无法逃离混境之地,则输出 No 。

走到(x,y),z表示是否使用喷气背包

当x,y,z固定时,具有唯一解,因此可以使用记忆化搜索

时间复杂度<x,y,z>三元组数量:1000*1000*2

from functools import lru_cache

#记忆化搜索2
@lru_cache(maxsize=None)
def dfs(x, y, z):
    # print(x, y, z)
    # 当前处于(x,y), z表示是否使用喷气背包
    # 如果能逃离, 返回True, 否则返回False
    if x == C - 1 and y == D - 1:
        return True
    #没走到终点,四个方向判断
    for i in range(4):
        xx, yy = x + dir[i][0], y + dir[i][1]
        #不能越界
        if xx < 0 or xx >= n or yy < 0 or yy >= m:
            continue
        #新坐标比旧坐标低,走到新坐标(xx,yy)
        if Map[xx][yy] < Map[x][y]:
            if dfs(xx, yy, z):
                return True
        #在(x,y)处使用喷气背包
        elif Map[xx][yy] < Map[x][y] + k and z == False:
            if dfs(xx, yy, True):
                return True
    return False


# 四个方向
dir = [(1, 0), (0, 1), (-1, 0), (0, -1)]
n, m, k = map(int, input().split())
A, B, C, D = map(int, input().split())
Map = []
for i in range(n):
    Map.append(list(map(int, input().split())))
if dfs(A - 1, B - 1, False):
    print("Yes")
else:
    print("No")

P216 地宫取宝

X 国王有一个地宫宝库。是 n×m 个格子的矩阵。每个格子放一件宝贝。每个宝贝贴着价值标签。

地宫的入口在左上角,出口在右下角。

小明被带到地宫的入口,国王要求他只能向右或向下行走。

走过某个格子时,如果那个格子中的宝贝价值比小明手中任意宝贝价值都大,小明就可以拿起它(当然,也可以不拿)。

当小明走到出口时,如果他手中的宝贝恰好是 k 件,则这些宝贝就可以送给小明。

请你帮小明算一算,在给定的局面下,他有多少种不同的行动方案能获得这 k 件宝贝。


输入描述

输入一行 3 个整数,用空格分开:n,m,k (1≤n,m≤50,1≤k≤12)。

接下来有 n 行数据,每行有m 个整数 Ci (0≤Ci≤12) 代表这个格子上的宝物的价值。

输出描述

要求输出一个整数,表示正好取 k 个宝贝的行动方案数。该数字可能很大,输出它对 10^9+7 取模的结果。

从(x,y)出发,先前已经有宝物z件,已有的最大宝物价值为w的方案数记为dfs(x,y,z,w)

只要确定四元组,就确定当前的方案数

答案=dfs(1,1,0,-1)

最终点=dfs(n,m,z,?) or dfs(n,m,z - 1,?)

(x,y)处可选,可不选,然后可以往右走或者往下走

from functools import lru_cache
   
@lru_cache(maxsize=None)   
def dfs(x, y, z, w):
    #从(x,y)出发,先前已有z件宝物,已有宝物最大价值为w
    #走到终点
    if x == n and y == m:
        #当前不需要选择
        if z == k:
            return 1
        #当前需要选择
        if z == k - 1:
            if w < a[x][y]:
                return 1
        #其他情况,均为0
        return 0
    ans = 0
    #遍历两个方向
    for delta_x, delta_y in [(1, 0), (0, 1)]:
        xx, yy = x + delta_x, y + delta_y
        if xx <= n and yy <= m:
            # 当前不选择
            ans += dfs(xx, yy, z, w)
            # 当前选择
            if w < a[x][y]:
                ans += dfs(xx, yy, z + 1, a[x][y])
    return ans % 1000000007
   
n, m, k = map(int, input().split())   
a = [[0]*(m+1)]   
for i in range(n):
    a.append([0] + list(map(int, input().split())))   
print(dfs(1, 1, 0, -1))

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/2297065.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

jenkins备份还原配置文件

下载ThinBackup插件 方式1 从插件市场直接下载 Manage Jenkins->Manage Plugins->可选插件搜索 注意&#xff1a;有时可能因为网络或者版本问题下载不了&#xff0c;好像是默认下载最新版本&#xff0c;可选择手动安装&#xff01; 方式二 手动安装插件 点击查看手…

02.11 数据库

1.思维导图 2.题目 将 epoll 服务器、客户端拿来用客户端&#xff1a;写一个界面&#xff0c;里面有注册登录服务器&#xff1a;处理注册和登录逻辑&#xff0c;注册的话将注册的账号密码写入数据库&#xff0c;登录的话查询数据库中是否存在账号&#xff0c;并验证密码是否正…

Spring Boot整合DeepSeek实现AI对话(API调用和本地部署)

本篇文章会分基于DeepSeek开放平台上的API&#xff0c;以及本地私有化部署DeepSeek R1模型两种方式来整合使用。 本地化私有部署可以参考这篇博文 全面认识了解DeepSeek利用ollama在本地部署、使用和体验deepseek-r1大模型 Spring版本选择 根据Spring官网的描述 Spring AI是一…

苹果转型独立AR眼镜:一场技术与创新的深度探索

在科技日新月异的今天,增强现实(AR)技术正逐渐从科幻电影走进我们的日常生活。作为科技界的领头羊,苹果公司的每一步动向都备受关注。近期,苹果宣布暂停原定的Mac连接式AR眼镜计划,转而全力研发一款独立的AR眼镜。这一战略调整不仅反映了苹果对AR市场的深度洞察,也预示着…

Java小白入门基础知识(一)

1.初识Java java源程序通过javac 编译生成字节码文件&#xff0c;通过java命令运行java程序 总结&#xff1a; 1&#xff09;在一个Java文件中&#xff0c;只能有一个public class 2&#xff09;public class一定要和文件名一致 3&#xff09;类里面包含方法 4&#xff09…

通过 Docker 安装和部署 KeyDB v6.3.4 的详细步骤

KeyDB 是一种高性能的开源内存数据库&#xff0c;最初是基于 Redis 项目开发的&#xff0c;但在性能、特性和功能上进行了许多增强和改进。它兼容 Redis 的大部分命令和数据结构&#xff0c;因此可以作为 Redis 的替代品使用&#xff0c;尤其是在需要更高性能和多线程支持的场景…

【JavaEE进阶】依赖注入 DI详解

目录 &#x1f334;什么是依赖注入 &#x1f384;依赖注入的三种方法 &#x1f6a9;属性注⼊(Field Injection) &#x1f6a9;Setter注入 &#x1f6a9;构造方法注入 &#x1f6a9;三种注⼊的优缺点 &#x1f333;Autowired存在的问题 &#x1f332;解决Autowired存在的…

Avnet RFSoC基于maltab得5G 毫米波 开发工具箱

使用 MATLAB 连接到 AMD Zynq™ RFSoC 评估板。使用 RF 附加卡执行 OTA 测试。使用 HDL Coder 部署算法 版本要求&#xff1a; 大于 2023b 需要以下支持包之一&#xff1a; 适用于 Xilinx 基于 Zynq 的无线电&#xff08;R2023b 及更早版本&#xff09;的通信工具箱支持包适…

neo4j-解决导入数据后出现:Database ‘xxxx‘ is unavailable. Run :sysinfo for more info.

目录 问题描述 解决方法 重新导入 问题描述 最近在linux上部署了neo4j&#xff0c;参照之前写的博客:neo4j-数据的导出和导入_neo4j数据导入导出-CSDN博客 进行了数据导出、导入操作。但是在进行导入后&#xff0c;重新登录网页版neo4j&#xff0c;发现对应的数据库状态变…

Mac(m1)本地部署deepseek-R1模型

1. 下载安装ollama 直接下载软件&#xff0c;下载完成之后&#xff0c;安装即可&#xff0c;安装完成之后&#xff0c;命令行中可出现ollama命令 2. 在ollama官网查看需要下载的模型下载命令 1. 在官网查看deepseek对应的模型 2. 选择使用电脑配置的模型 3. copy 对应模型的安…

【分布式理论9】分布式协同:分布式系统进程互斥与互斥算法

文章目录 一、互斥问题及分布式系统的特性二、分布式互斥算法1. 集中互斥算法调用流程优缺点 2. 基于许可的互斥算法&#xff08;Lamport 算法&#xff09;调用流程优缺点 3. 令牌环互斥算法调用流程优缺点 三、三种算法对比 在分布式系统中&#xff0c;多个应用服务可能会同时…

第一财经对话东土科技 | 探索工业科技新边界

当前以ChatGPT、Sora等为代表的生成式人工智能快速发展&#xff0c;越来越多面向垂直场景的行业大模型涌现出来&#xff0c;并成为推动制造业智能化改造与数字化转型、加快推进新型工业化&#xff0c;进而培育发展新质生产力的新引擎。 在垂类场景的应用落地&#xff0c;是AI发…

深入理解Java对接DeepSeek

其实&#xff0c;整个对接过程很简单&#xff0c;就四步&#xff0c;获取key&#xff0c;找到接口文档&#xff0c;接口测试&#xff0c;代码对接。 1.获取 KEY https://platform.deepseek.com/transactions 直接付款就是了&#xff08;现在官网暂停充值2025年2月7日&#xf…

线段平移 实战笔记

目录 pingyi2.py pingyi2.py import numpy as np import cv2# 画线段的函数 def draw_line(img, p1, p2, color, thickness=2):cv2.line(img, tuple(p1), tuple(p2), color, thickness)# 创建图像并初始化 def create_image():# 创建一个黑色背景图像img = np.zeros((500, 50…

WinForm 防破解、反编译设计文档

一、引言 1.1 文档目的 本设计文档旨在阐述 WinForm 应用程序防破解、反编译的设计方案&#xff0c;为开发团队提供详细的技术指导&#xff0c;确保软件的知识产权和商业利益得到有效保护。 1.2 背景 随着软件行业的发展&#xff0c;软件破解和反编译现象日益严重。WinForm…

DeepSeek应用——与word的配套使用

目录 一、效果展示 二、配置方法 三、使用方法 四、注意事项 1、永久化使用 2、宏被禁用 3、office的生成失败 记录自己学习应用DeepSeek的过程...... 这个是与WPS配套使用的过程&#xff0c;office的与这个类似&#xff1a; 一、效果展示 二、配置方法 1、在最上方的…

利用邮件合并将Excel的信息转为Word(单个测试用例转Word)

利用邮件合并将Excel的信息转为Word 效果一览效果前效果后 场景及问题解决方案 一、准备工作准备Excel数据源准备Word模板 二、邮件合并操作步骤连接Excel数据源插入合并域预览并生成合并文档 效果一览 效果前 效果后 场景及问题 在执行项目时的验收阶段&#xff0c;对于测试…

OpenCV 相机标定流程指南

OpenCV 相机标定流程指南 前置准备标定流程结果输出与验证建议源代码 OpenCV 相机标定流程指南 https://docs.opencv.org/4.x/dc/dbb/tutorial_py_calibration.html https://learnopencv.com/camera-calibration-using-opencv/ 前置准备 制作标定板&#xff1a;生成高精度棋…

网络在线考试|基于vue的网络在线考试系统的设计与实现(源码+数据库+文档)

网络在线考试系统 目录 基于SSM&#xff0b;vue的网络在线考试系统的设计与实现 一、前言 二、系统设计 三、系统功能设计 1功能页面实现 2系统功能模块 3管理员功能模块 4学生功能模块 四、数据库设计 五、核心代码 六、论文参考 七、最新计算机毕设选题推荐 八…

DEEPSEEK与GPT等AI技术在机床数据采集与数字化转型中的应用与影响

随着人工智能&#xff08;AI&#xff09;技术的迅猛发展&#xff0c;深度学习、自然语言处理等先进技术开始广泛应用于各行各业。在制造业尤其是机床行业&#xff0c;AI技术的融合带来了巨大的变革&#xff0c;尤其在机床数据采集与机床数字化方面的应用。本文将探讨DEEPSEEK、…