DFS剪枝

news2025/1/16 14:49:18

目录

一、前言

二、剪枝

1、概念

2、类别

三、例题

1、剪格子(lanqiaoOJ题号211)

2、路径之谜(2016年决赛,lanqiaoOJ题号89)

3、四阶幻方(2015年决赛,lanqiaoOJ题号689)

4、分考场(2017年决赛,lanqiaoOJ题号109)


一、前言

本文主要讲了剪枝的概念、类别与DFS的一些例题。

二、剪枝

1、概念

剪枝:把不会产生答案的,或不必要的枝条“剪掉”。

剪枝的关键:剪什么枝、在哪里减。

剪枝是搜索常用的优化手段,常常能把指数级的复杂度,优化到近似多项式的复杂度。

2、类别

  • 可行性剪枝:对当前状态进行检查,如果当前条件不合法就不再继续,直接返回。
  • 搜索顺序剪枝:搜索树有多个层次和分支,不同的搜索顺序会产生不同的搜索树形态。
  • 最优性剪枝:在最优化问题的搜索过程中,如果当前花费的代价已超过前面搜索到的最优解,那么本次搜索已经没有继续进行下去的意义,停止对当前分支的搜索。
  • 排除等效冗余:搜索的不同分支,最后的结果是一样的,那么只搜一个分支就够了。
  • 记忆化搜索:在递归的过程中,有许多分支被反复计算,会大大降低算法的执行效率。将已经计算出来的结果保存起来,以后需要用到的时候直接取出结果,避免重复运算,从而提高了算法的效率。

三、例题

1、剪格子(lanqiaoOJ题号211)

【题目描述】

如下图所示,3×3 的格子中填写了一些整数。

沿着图中的红色线剪开,得到两个部分,每个部分的数字和都是 60。请你编程判定:对给定的 m×n 的格子中的整数,是否可以分割为两个部分,使得这两个区域的数字和相等。如果存在多种解答,请输出包含左上角格子的那个区域包含的格子的最小数目。无法分割输出 0。

【输入描述】

第一行是 2 个整数 m,n,表示表格的宽度和高度。后面 n 行,每行 m 个正整数。

【输出描述】

在所有解中,包含左上角的分割区可能包含的最小的格子数目。

这是一道典型的 DFS 题。

  • 思路:先求所有格子的和 sum,然后用 DFS 找一个联通区域,看这个区域的和是否为 sum/2。
  • 剪枝:如果 DFS 到的部分区域的和已经超过 sum/2,就不用继续 DFS 了。
  • 这种格子DFS搜索题,是蓝桥杯的常见考题
def dfs(x,y,c,s):
    global sum_num,ans
    if 2*s>sum_num:     #剪枝
        return
    if 2*s==sum_num:    #终止条件
        if ans>c and vis[0][0]==1:
            ans=c
        return
    vis[x][y]=1         #保存现场
    dir=[(1,0),(-1,0),(0,-1),(0,1)]
    for u,v in dir:     #遍历
        tx,ty=x+u,y+v
        if tx>=0 and tx<=n-1 and ty>=0 and ty<=m-1:
            if vis[tx][ty]==0:
                dfs(tx,ty,c+1,s+a[x][y])
    vis[x][y]=0         #恢复现场
    
m,n=map(int,input().split())
a=[list(map(int,input().split())) for _ in range(n)]    #输入矩阵
vis=[[0]*m for _ in range(n)]
sum_num=0
for i in a:
    sum_num+=sum(i)
ans=1000000
dfs(0,0,0,0)
print(ans)

2、路径之谜(2016年决赛,lanqiaoOJ题号89)

【题目描述】

小明冒充骑士进入了一个城堡。城堡里边方形石头铺成的地面。假设城堡地面是 n×n 个方格。按习俗,骑士要从西北角走到东南角。可以横向或纵向移动,但不能斜着走,也不能跳跃。每走到一个新方格,就要向正北方和正西方各射一箭。(城堡的西墙和北墙内各有 n 个靶子) 同一个方格只允许经过一次。但不必走完所有的方格。如果只给出靶子上箭的数目,你能推断出骑士的行走路线吗?

本题的要求就是已知箭靶数字,求骑士的行走路径 (测试数据保证路径唯一)

【输入格式】

第一行一个整数 N (0<N<20),表示地面有 N×N 个方格;第二行 N 个整数,空格分开,表示北边的箭靶上的数字 (自西向东);第三行 N 个整数,空格分开,表示西边的箭靶上的数字 (自北向南)。

【输出格式】

一行若干个整数,表示骑士路径。为了方便表示,我们约定每个小格子用一个数字代表,从西北角 (左上角) 开始编号:0,1,2,3 ....

【输入示例】

4

2 4 3 4

4 3 3 3

【输出示例】

0 4 5 1 2 3 7 11 10 9 13 14 15

DFS:题目要求输出一条路径,用 DFS 很合适, DFS 搜索过程中,自然生成一条路径。

剪枝:每走到一个格子,对应的靶子上箭多一支,靶子上的箭等于给定的数字后,就不用再 DFS 下去了。(或者做减法,靶子的数字减到 0)

记录路径的技巧。根据题目的要求,用栈来跟踪DFS的过程,记录DFS走过的路径,是最方便的。DFS到某个格子时,把这个格子放到栈里,表示路径增加了这个格子。DFS 回溯的时候,退出了这个格子,表示路径上不再包括这个格子,需要从栈中弹走这个格子。

def dfs(x,y):
    if a[x]<0 or b[y]<0:    #剪枝:数字减到0
        return
    if x==n-1 and y==n-1:   #终止条件:到达终点
        ok=1
        for i in range(n):
            if a[i]!=0 or b[i]!=0:
                ok=0
                return
        if ok==1:           #成功找出路径并输出
            for i in range(len(path)):
                print(path[i],end=' ')
    for u,v in [(1,0),(-1,0),(0,1),(0,-1)]:     #遍历
        tx,ty=x+u,y+v
        if 0<=tx<n and 0<=ty<n and vis[tx][ty]==0:
            vis[tx][ty]=1
            path.apppend(tx*n+ty)   #进栈,记录路径
            a[tx]-=1                #根据题意,这里箭数要减 1
            b[ty]-=1
            dfs(tx,ty)
            path.pop()              #出栈,DFS回溯
            a[tx]+=1
            b[ty]+=1
            vis[tx][ty]=0
            
n=int(input())
vis=[[0]*n for i in range(n)]
path=[]     #用栈记录路径
path.appendd(0)
b=list(map(int,input().split()))    #输入北边和西边的靶子
a=list(map(int,input().split()))
vis[0][0]=1
a[0]-=1
b[0]-=1     #从左上角出发
dfs(0,0)

3、四阶幻方(2015年决赛,lanqiaoOJ题号689)

【题目描述】

把 1~16 的数字填入 4×4 的方格中,使得行、列以及两个对角线的和都相等,满足这样的特征时称为:四阶幻方。

四阶幻方可能有很多方案。如果固定左上角为 1,请计算一共有多少种方案。

除了 1,数字 2~16 有 15! = 1.3×10^12 种排列,无法把所有排列都试一遍。

剪枝:每种排列,只要前面一些数字不适合,就不用再计算下去了。

需要自写排列。

def dfs(n):
    global cnt
    if n>=4 and m[0]+m[1]+m[2]+m[3]!=34:
        return
    if n>=7 and m[0]+m[4]+m[5]+m[6]!=34:
        return
    if n>=10 and m[1]+m[7]+m[8]+m[9]!=34:
        return
    if n>=11 and m[3]+m[6]+m[8]+m[10]!=34:
        return
    if n>=12 and m[4]+m[7]+m[10]+m[11]!=34:
        return
    if n>=14 and m[5]+m[8]+m[12]+m[13]!=34:
        return
    if n>=15 and m[2]+m[10]+m[12]+m[14]!=34:
        return
    if n>=16 and (m[6]+m[9]+m[14]+m[15]!=34 \
                  or m[3]+m[11]+m[13]+m[15]!=34 \
                  or m[0]+m[7]+m[12]+m[15]!=34):
        return      #上面所有的条件判断都是剪枝
    if n==16:
        cnt+=1
    for i in range(2,17):   #2~16的全排列
        if vis[i]==0:
            m[n]=i
            vis[i]=1
            dfs(n+1)
            vis[i]=0

cnt=0
m=[0]*17    #用一维数组表示幻方
m[0]=1      # 1 被固定
vis=[0]*17
vis[1]=1
dfs(1)
print(cnt)

4、分考场(2017年决赛,lanqiaoOJ题号109)

【题目描述】

n 个人参加考试。为了公平,要求任何两个认识的人不能分在同一个考场。求最少需要分几个考场才能满足条件。

【输入格式】

第一行,一个整数 n (1<n<100),表示参加考试的人数。

第二行,一个整数 m,表示接下来有 m 行数据。以下 m 行每行的格式为:两个整数 a, b,用空格分开 (1<=a, b<=n) 表示第 a 个人与第 b 个人认识(编号从1开始)。

【输出格式】

一行一个整数,表示最少分几个考场。

【输入】

5

8

1 2

1 3

1 4

2 3

2 4

2 5

3 4

4 5

【输出】

4

【思路】

  • 从第 1 个考场开始,逐个加入考生。每新加进来一个人 x,都与已经开设的考场里面的人进行对比,如果认识,就换个考场。直到找到一个考场,考场里面所有的人都不认识x,x就可以坐在这里。如果所有已经开设的考场都有熟人,就开一个新考场给 x 坐。
  • 这个模拟的结果是得到了一个可行的考场安排,但这个安排的考场数量不一定是最少的。
  • 题目求最少考场数量,需要把所有可能的考场安排都暴力地试一遍,找到最少的那个考场安排。
  • 用 DFS 搜索所有可能的情况,得到最少考场。暴力搜索所有的考场安排,计算量很大。
  • 剪枝:用剪枝来减少搜索。在搜索一种新的可能的考场安排时,如果需要的考场数量已经超过了原来某个可行的考场安排,就停止。
def dfs(x,room):
    global num,p
    if room>num:    #剪枝: 需要的考场数量已经超过了原来某个可行的考场安排,停止
        return
    if x>n:         #终止条件
        if room<num:
            num=room
        return
    for j in range(1,room+1):   #遍历
        k=0
        while p[j][k] and a[x][p[j][k]]==0:
            k+=1
        if p[j][k]==0:
            p[j][k]=x
            dfs(x+1,room)
            p[j][k]=0
    p[room+1][0]=x
    dfs(x+1,room+1)
    p[room+1][0]=0

n=int(input())
m=int(input())
num=110
p=[[0 for i in range(n+1)] for j in range(n+1)]
a=[[0 for i in range(n+1)] for j in range(n+1)]
for i in range(m):
    u,v=map(int,input().split())
    a[u][v]=a[v][u]=1
dfs(1,0)
print(num)

补充:DFS习题:

以上,DFS剪枝

祝好

 

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

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

相关文章

P1028 [NOIP2001 普及组] 数的计算————C++

题目 [NOIP2001 普及组] 数的计算 题目描述 给出自然数 nnn&#xff0c;要求按如下方式构造数列&#xff1a; 只有一个数字 nnn 的数列是一个合法的数列。在一个合法的数列的末尾加入一个自然数&#xff0c;但是这个自然数不能超过该数列最后一项的一半&#xff0c;可以得到…

linux(debian系列)配置seetaface6

seetaface6依赖于opencv&#xff0c;另外我们需要界面&#xff0c;所以也需要Qt&#xff08;你也可以选择其他的&#xff09;。 这里的目标是配置好环境&#xff0c;能够编译并运行seetaface6给的demo。 那个demo中用到了sqlite数据库&#xff0c;所以我们还需要安装sqlite。…

Cosmos 基础(一)

Cosmos 区块链互联网 Cosmos是一个不断扩展的生态系统&#xff0c;由相互连接的应用程序和服务组成&#xff0c;为去中心化的未来而构建。 Cosmos 应用程序和服务使用IBC(the Inter-Blockchain Communication protocol, 区块链间通信协议)连接。这一创新使您能够在主权国家之…

僵尸进程孤儿进程

目录 1. 僵尸进程 2. 孤儿进程 1. 僵尸进程 僵尸状态&#xff1a;一个进程已经退出&#xff0c;但是还不允许被OS释放&#xff0c;处于一个被检测的状态。 僵死状态&#xff08;Z-Zombies&#xff09;是一个比较特殊的状态。当子进程退出并且父进程没有读取到子进程退出的返…

学习记录663@项目管理之项目范围管理

什么是项目范围管理 项目范围管理包括确保项目做且只做所需的全部工作&#xff0c;以成功完成项目的各个过程。它关注的焦点是:什么是包括在项目之内的&#xff0c;什么是不包括在项目之内的&#xff0c;即为项目工作明确划定边界。通俗地讲&#xff0c;项目范围管理就是要做范…

Dubbo框架学习(第二章Dubbo3拥抱云原生)

由于在微服务领域有两大框架统治&#xff0c;一个是springCloud的全家桶&#xff0c;一个是Dubbo。我用Dubbo比较少&#xff0c;所以也是学习状态。Dubbo框架学习&#xff0c;资料来源于cn.dubbo.apache.org。第二章Dubbo3拥抱云原生新一代的 Triple 协议基于 HTTP/2 作为传输层…

计算机网络第五章、第六章

1.传输层定义&#xff1a;为应用层提供通信服务使用网络层的服务传输层的功能&#xff1a;传输层提供进程和进程之间的逻辑通信复用和分用传输层对收到的报文进行差错检测传输层的两种协议传输层的两个协议&#xff1a;1.面向连接的传输控制协议TCP&#xff1a;传送数据之前必须…

人工智能辅助药物发现(1)肿瘤靶点识别

目录基于AI的靶点识别概述肿瘤建模肿瘤转录组转录组异质性单细胞转录组数据分析单细胞表观肿瘤模型肿瘤中的表观遗传模型基于甲基化测序技术的肿瘤表观遗传模型基于染色质可及性的肿瘤表观遗传模型多模态肿瘤建模靶点识别基于单细胞RNA的靶点发现基于表观的靶点发现基于多组学的…

点击化学交联剂N3-SS-N3,叠氮-二硫键-叠氮简介,CAS:352305-38-5

英文名称&#xff1a;Azidoethyl-SS-ethylazide N3-SS-N3 N3-S-S-N3 化学式&#xff1a;C4H8N6S2 分子量&#xff1a;204.3 CAS&#xff1a;352305-38-5 存储条件&#xff1a;-20C&#xff0c;避光&#xff0c;避湿 用 途&#xff1a;仅供科研实验使用&#xff0c;不用于…

《Buildozer打包实战指南》第四节 正式打包一个apk文件

目录 4.1 了解buildozer.spec配置文件中的常用参数 4.2 修改配置文件打包apk 在上一节内容中&#xff0c;我们配置好了打包环境&#xff0c;还顺带成功打包出了一个apk文件&#xff08;读者可以把这个apk安装到手机上运行&#xff09;。不过在打包这个apk前&#xff0c;我们没…

Unity学习笔记--如何在Unity运行cmd?并且在Unity中利用cmd操作svn(例如生成svn--diff文件)

目录前言需求拆分解决方案实操前言 最近工作有一个需求&#xff0c;简单来说是在 Unity 里面动态获取 SVN diff 的数据&#xff0c;并且针对这些 diff 文件做对应操作 需求拆分 我们首先拆分下需求 Unity里面获取 SVN 的 diff 文件针对生成的 diff 文件做解析&#xff0c;找…

Qt 视频播放

一、简介Qt对音视频的播放和控制&#xff0c;相机拍照&#xff0c;收音机等多媒体应用提供了强大的支持。Qt5使用了全新的Qt Multimedia模块来实现多媒体应用&#xff0c;而原来Qt4中用于实现多媒体功能的Phonon模块已经被移除。新的Qt Multimedia模块提供了丰富的接口&#xf…

【从零开始学习深度学习】50.Pytorch_NLP项目实战:卷积神经网络textCNN在文本情感分类的运用

在之前介绍的“卷积神经网络”中我们探究了如何使用二维卷积神经网络来处理二维图像数据。在语言模型和文本分类任务中&#xff0c;我们将文本数据看作是只有一个维度的时间序列&#xff0c;并很自然地使用循环神经网络来表征这样的数据。其实&#xff0c;我们也可以将文本当作…

安装 MySQL

1.下载安装文件 访问MySQL官网下载安装文件。 如下图所示&#xff0c;点击页面中的“DOWNLOAD”按钮。 点击下载之后自动进行下载。下载到本地的文件名称为&#xff1a;mysql-8.0.31-winx64.zip 2.解压安装文件 将压缩文件解压到你安装的目录&#xff0c;比如&#xff1a;F:…

数据结构与算法笔记

0 核心框架汇总 框架思维 数据结构的存储方式只有两种&#xff1a;数组&#xff08;顺序存储&#xff09;和链表&#xff08;链式存储&#xff09; 算法 数学中的算法重在推导&#xff0c;计算机中的算法重在穷举 计算机算法的本质特点&#xff1a; 穷举 穷举有两个关键难点…

Linux环境 java应用问题排查

0&#xff09;查看CPU占用高的进程PID top -d 1 或 top -H 注&#xff1a; top -d 1 中的 1 是数字&#xff1a;1&#xff0c;不是字母 &#xff1a;l 。 1&#xff09;查看内存使用情况&#xff08;memory-info.log为具体文件路径&#xff09; jmap -heap PID > memory-i…

某程序员跳槽涨薪50%!网友:不合理~

在IT届&#xff0c;有个传闻&#xff0c;跳槽就是程序猿涨工资最好的方式。大家认为程序员跳槽要求涨薪50%合理吗&#xff1f;有人说&#xff1a;凭本事涨的为啥不合理&#xff01;01程序员跳槽要求涨薪50%过分吗&#xff1f;在知乎上看到这样一个帖子&#xff0c;有人提问“程…

CANoe 15版本中CAPL代码自动补全功能的小Bug

最近在使用CANoe 15版本的软件编写CAPL脚本时,遇到了一些小的困扰,记录下来分享给大家! 当我在capl函数中要传入两个参数时,除了逗号隔开两个参数外,还希望有一个空格能进一步拉开两个参数的距离,增加代码的可读性 但是,传入第一个参数后,输入逗号,此时capl的自动补…

IP 网络主动监测系统 Renix Active

一、IT网络运维面临的挑战​ 1.网络性能可视化​ • 与公有云和SaaS平台连接的可靠性​ • 广域网线路性能​ • 互联网专线性能​ 2.诊断工具​ • 现场无IT工程师覆盖​ • 诊断的人力费用​ • 网络与应用系统的纠结​ 3.用户体验​ • Web应用的访问质量​ • 语…

C++设计模式(2)——工厂方法模式

亦称&#xff1a; 虚拟构造函数、Virtual Constructor、Factory Method 意图 工厂方法模式是一种创建型设计模式&#xff0c; 其在父类中提供一个创建对象的方法&#xff0c; 允许子类决定实例化对象的类型。 问题 假设你正在开发一款物流管理应用。 最初版本只能处理卡车…