2025dsfz集训Day3:DFS搜索与剪枝
DAY3: DFS搜索与剪枝
\]
特别感谢 此次课的主讲 - Kwling
深搜
- 深度优先搜索(DFS)是一种遍历或搜索树或图的算法,它从一个根节点开始,尽可能深地搜索每个分支,直到找到解为止。在搜索讨程中,为了提高效率,减少不必要的搜索,通常会采用各种剪枝优化策略。
剪枝基本思想
- 在深度优先搜索中,我们通常会遍历图或树的所有节点和边,直到找到解或者遍历完所有节点。然而,某些情况下,我们知道某些路径不会导致有效的解,可以提前停止进一步的搜索,这就是剪枝。通过合理的剪枝策略,我们可以避免不必要的递归和计算极大地提高算法的效率
剪枝方法:
- 最优化剪枝
- 对应问题:求最优策略或最优答案类题目
- 如果当前答案比之前记录的已有答案还差的话,就直接减掉。
- 优化搜索顺序
- 在一些搜索问题中,搜索树的各个层次、各个分支之间的顺序不是固定的。不同的搜索顺序会产生不同的搜索树形态,其规模大小也相差甚远。
- 大部分情况下,我们应该优先搜索分支较少的节点,(说白了就是先搜小的分支,因为先搜大的浪费时间,详见P1223 排队接水)
- 可行性剪枝
- 在搜索过程中,及时对当前状态进行检查,如果发现分支已经无法到达递归边界,就执行回溯。
(某些题目条件的范围限制是一个区间,此时可行性剪枝也被称为 "上下界剪枝" )
- 在搜索过程中,及时对当前状态进行检查,如果发现分支已经无法到达递归边界,就执行回溯。
- 记忆化搜索
- 可以记录每个状态的搜索结果,在重复调历一个状态时直接检索并返回。(就好像我们对图进行深度优先遍历时,标记一个节点是否已经被访问过。
- 例题:
- 洛谷P10483 | 小猫爬山
剪枝题目思路&代码
\(T1\)
- AccodersP1862 |【一本通基础搜索与回溯】红与黑
思路: 一道 \(DFS\) 经典例题,类似迷宫问题,非常简单,要注意输入格式是先列后行!
#include <bits/stdc++.h>
using namespace std;
char a[1010][1010];
int n,m;
int ans = 1;
bool f[1010][1010];
void dfs(int x,int y){
int dx[5] = {0,0,-1,0,1};
int dy[5] = {0,1,0,-1,0};
for(int i = 1;i <= 4;i++){
int xx = x+dx[i],yy = y+dy[i];
if(xx >= 1 && xx <= m && yy >= 1 && yy <= n && f[xx][yy] == false){
if(a[xx][yy] == '.'){
f[xx][yy] = true;
dfs(xx,yy);
ans++;
}
}
}
}
int main(Designer = 洛谷@Lwj54joy,uid=845400){
while(1){
ans = 1;
memset(f,false,sizeof(f));
cin>>n>>m;
if(n == 0 and m == 0) return 0;
int x,y;
for(int i = 1;i <= m;i++){
for(int j = 1;j <= n;j++) {
cin>>a[i][j];
if(a[i][j] == '@') x = i,y = j;
}
}
dfs(x,y);
// for(int i = 1;i <= n;i++){
// for(int j = 1;j <= m;j++){
// if(f[i][j] && a[i][j] == '.') ans++;
// }
// }
cout<<ans<<endl;
}
}
\(T2\)
- AccodersP1865 |【一本通基础搜索与回溯】马走日
思路: \(DFS\) 经典例题,暴力就行,千万注意数据范围是 \(0\) ~ \(n-1\) !
#include <bits/stdc++.h>
using namespace std;
int n,m;
int ans;
bool f[1010][1010];
void dfs(int x,int y,int st){
int dx[15] = {0,1,2,2,1,-1,-2,-2,-1};
int dy[15] = {0,2,1,-1,-2,-2,-1,1,2};
if(st == n*m) {ans++;return ;}
for(int i = 1;i <= 8;i++){
int xx = x+dx[i],yy = y+dy[i];
if(xx >= 0 && xx < n && yy >= 0 && yy < m && f[xx][yy] == false){
f[xx][yy] = true;
dfs(xx,yy,st+1);
f[xx][yy] = false;
}
}
}
int main(Designer = 洛谷@Lwj54joy,uid=845400){
int T;
cin>>T;
while(T--){
memset(f,0,sizeof(f));
ans = 0;
cin>>n>>m;
int x,y;
cin>>x>>y;
f[x][y] = 1;
dfs(x,y,1);
cout<<ans<<endl;
}
}
\(T3\)
洛谷P10483 | 小猫爬山
思路: 猫猫不可以劈开!猫猫不可以劈开!!猫猫不可以劈开!!!如果你看了讨论区,你会发现有一种做法,先加上所有猫的体重然后除以缆车的载重。但是很明显这样有时候猫就会被切开。
所以正解是 DFS,我们可以把每一辆车上的目前总重量记录下来,然后对于每一只猫枚举每一辆车,要么蹭上一辆车继续 \(DFS\) ,要么自己新开一辆车 dfs。也可以再加一个最优性剪枝(即如果当前车数大于已知最优解就直接结束)。我这里还用了一个从大到小排序的优化,因为越小的越容易蹭车,不用开新车嘛。
#include<bits/stdc++.h>
using namespace std;
int n,m,a[101],ans,sum[101];
void dfs(int u,int v){
if(v>=ans) return;
if(u==n){
ans=v;
return;
}
for(int i=0;i<v;i++){
if(sum[i]+a[u]<=m){
sum[i]+=a[u];
dfs(u+1,v);
sum[i]-=a[u];
}
}
sum[v]=a[u];
dfs(u+1,v+1);
sum[v]=0;
}
int main(Designer = 洛谷@Lwj54joy,uid=845400){
cin>>n>>m;
ans=n;
for(int i=0;i<n;i++) cin>>a[i];
sort(a,a+n,greater<int>());
dfs(0,0);
cout<<ans;
return 0;
}
\(T4\)
- 洛谷P1784 | 数独
- AccodersP2513 |【算法进阶 搜索深度优先搜索】Sudoku
- 洛谷P1048 | Sudoku
思路: 方阵序号 \(=(\) 行数 \(-1)3\times3+(\) 列数 \(-1)\div3+1\)- 注意! 行数列数要减 \(1\) ,因为 \(3\) 的整数倍数除以 \(3\) 会比原方阵大 \(1\) ,不能满足上述需求。详见洛谷题解 P1784【数独】
#include <bits/stdc++.h>
using namespace std;
bool is_run;
int sd[11][11];//数独方阵定义
bool p[11][11],l[11][11],fz[11][11];//行(排),列,方阵。
void _out(){//输出
is_run = 0;
for(int i=1;i<=9;i++){
for(int j=1;j<=9;j++)
cout<<sd[i][j];
cout<<endl;
}
}
void dfs(int x,int y){//深搜
if(is_run == 0) return ;
if(sd[x][y]!=0)//如果原来这个位置有数字,跳过。
if(x==9&&y==9){
_out();//全部填完,输出
is_run = 0;
return ;
}else if(y==9)dfs(x+1,1);//当列数为9,搜索下一排。
else dfs(x,y+1);//深搜搜下一列
else//原来的地方没有数字就填充
for(int i=1;i<=9;i++)
if((!p[x][i])&&(!l[y][i])&&(!fz[(x-1)/3*3+(y-1)/3+1][i])){//判断是不是重复了
sd[x][y]=i;//填充
p[x][i]=l[y][i]=fz[(x-1)/3*3+(y-1)/3+1][i]=true;//打上标记
if(x==9&&y==9){
_out();//全部填完,输出
is_run = 0;
return ;
}else if(y==9)dfs(x+1,1);//填完本行(列),开始搜下一行
else dfs(x,y+1);//搜下一列
sd[x][y]=0; //恢复标记。(回溯)
p[x][i]=l[y][i]=fz[(x-1)/3*3+(y-1)/3+1][i]=false;//恢复标记(回溯)
}
}
int main(Designer = 洛谷@Lwj54joy,uid=845400){
int T;
cin>>T;
while(T--){
is_run = true;
memset(sd,0,sizeof(sd));
memset(p,0,sizeof(p));
memset(l,0,sizeof(l));
memset(fz,0,sizeof(fz));
for(int i=1;i<=9;i++){
for(int j=1;j<=9;j++){
char tt;
cin>>tt;
int t = tt-'0';
if(t!=0) p[i][t]=l[j][t]=fz[(i-1)/3*3+(j-1)/3+1][t]=true;
//填充的不是0的话,表示原来有数字了。打上标记。
sd[i][j]=t;//填充进数独
}
}
dfs(1,1);//搜
}
return 0;
}
\(T5\)
\(T6\)
\(T7\)
洛谷T148457 | 生日蛋糕数据加强版
思路: 果断DFS,所以怎么去搜呢?我们先要解决一个问题:表面积怎么算?数学好的同学已经看出来了,就是每一层的侧面积(S=2pirh)加上最下面一层的底面积(S=pi*r^2),消去pi就好了
首先建立整体的框架:dfs函数从下往上搜,标记当前的层数、已使用过的总表面积和体积(这个是便于剪枝)、下方一层的半径和高度(因为上面一层需要使用)。从第M层开始搜,到第0层时判断已使用体积是否等于总体积。
整体框架出来了,但这么打肯定TLE。所以我们要去剪枝。
因为蛋糕要上小下大,我们假设第一层(最上面一层)的半径为1,高度为1(不能再小了),那么第二层高度就要为 \(2\) ,半径也至少为 \(2\),以此类推,那么我们自然可以得出自上而下前 \(n\)层的最小体积、最小表面积。那么我们每次 \(DFS\) 时就可以判断:如果当前已使用总体积+上方层数的最小体积>输入要求的体积,就直接剪掉;
如果当前已使用总表面积+上方层数的最小表面积>最优解,就直接剪掉(最优化剪枝)。
应用上述剪枝可以跑过 \(7\) 个点( \(3\) 个 \(TLE\) )
因为我们只对表面积和体积进行了分别判断,而这二者明显是有内在联系的。
体积 \(V=pi\times r\times r\times h\) ,侧面积 \(S=2\times pi\times r\times h\) ,可推证出若体积一定,半径越大表面积就越小(相对)
所以我们可以假设剩余的体积全部以当前层的半径做成蛋糕,因为当前的半径的最大的,所以得出的表面积是最小的,根据这个“最小表面积”判断表面积是否超过最优解,超出则剪掉(最优化剪枝)
#include<cstdio>
#include<cmath>
#include<algorithm>
#define oo 2147483647
using namespace std;
int NN,M,N,ans=oo;
int ss[21],sv[21];
inline void dfs(int t,int S,int V,int lR,int lH)//层数,已用总面积,已用总体积,上一层半径 ,上一层高度
{
if (t==0)
{
if (V==N) ans=min(ans,S);
return ;
}
if (V+sv[t]>N) return ;//分别对应上述3个剪枝,其中2、3是最优化剪枝
if (S+ss[t]>ans) return ;
if (S+2*(N-V)/lR>ans) return ;
for (int r=lR-1;r>=t;r--)
{
if (t==M) S=r*r;
int maxh=min((N-V-sv[t-1])/(r*r),lH-1);
for (int h=maxh;h>=t;h--)
{
dfs(t-1,S+2*r*h,V+r*r*h,r,h);
}
}
}
int main()
{
scanf("%d %d",&N,&M);
for (int i=1;i<=M;i++)
{
ss[i]=2*i*i;//计算出每一层的最小侧面积
ss[i]+=ss[i-1];//要求前缀和
sv[i]=i*i*i;//最小体积
sv[i]+=sv[i-1];//要求前缀和
}
dfs(M,0,0,sqrt(N),N);//第一层的高度最小为1,所以半径最大就是根号N
if (ans==oo) printf("0");
else printf("%d",ans);
return 0;
}
\(T8\)
todo
\(T9\)
todo
\(\huge Thanks.\)
感谢阅读,若有问题或错误,欢迎评论(或私信我)。
2025 Designed By @洛谷Lwj54joy,uid=845400
2025dsfz集训Day3:DFS搜索与剪枝的更多相关文章
- HDU 1010 (DFS搜索+奇偶剪枝)
题目链接: http://acm.hdu.edu.cn/showproblem.php?pid=1010 题目大意:给定起点和终点,问刚好在t步时能否到达终点. 解题思路: 4个剪枝. ①dep&g ...
- HZNU-ACM寒假集训Day3小结 搜索
简单搜索 1.DFS UVA 548 树 1.可以用数组方式实现二叉树,在申请结点时仍用“动态化静态”的思想,写newnode函数 2.给定二叉树的中序遍历和后序遍历,可以构造出这棵二叉树,方法是根据 ...
- ICPC Asia Nanning 2017 I. Rake It In (DFS+贪心 或 对抗搜索+Alpha-Beta剪枝)
题目链接:Rake It In 比赛链接:ICPC Asia Nanning 2017 Description The designers have come up with a new simple ...
- 搜索(剪枝优化):HDU 5113 Black And White
Description In mathematics, the four color theorem, or the four color map theorem, states that, give ...
- 【LOJ#6066】「2017 山东一轮集训 Day3」第二题(哈希,二分)
[LOJ#6066]「2017 山东一轮集训 Day3」第二题(哈希,二分) 题面 LOJ 题解 要哈希是很显然的,那么就考虑哈希什么... 要找一个东西可以表示一棵树,所以我们找到了括号序列. 那么 ...
- 蓝桥杯dfs搜索专题
2018激光样式 #include<bits/stdc++.h> using namespace std; /* dfs(i) 第i个激光机器 有两种选择:vis[i-1] == 0 时 ...
- [CF293B]Distinct Paths_搜索_剪枝
Distinct Paths 题目链接:http://codeforces.com/problemset/problem/293/B 数据范围:略. 题解: 带搜索的剪枝.... 想不到吧..... ...
- LeetCode DFS搜索与回溯专题
DFS + 回溯专题 17. 电话号码的字母组合 迭代也可以实现搜索 循环改写dfs搜索的写法: 例如 C++写法 class Solution { public: vector<string& ...
- hdu 1312:Red and Black(DFS搜索,入门题)
Red and Black Time Limit: 2000/1000 MS (Java/Others) Memory Limit: 65536/32768 K (Java/Others)Tot ...
- [ZOJ 1011] NTA (dfs搜索)
题目链接:http://acm.zju.edu.cn/onlinejudge/showProblem.do?problemCode=1011 题目大意:在一棵树上,给你起始状态,问你能否到达终止状态. ...
随机推荐
- uni-app之vuex(一)
如何获取到vuex中data的值 uni-app中内置了 vuex 所以可以直接去使用哈. 在项目的跟目录下 创建文件夹store 在store目录下创建index.js index.js目录如下 i ...
- DBeaver连接SqlServer报“The server selected protocol version TLS10 is not accepted by client prefere”的错误
1.问题描述 DBeaver在连接SqlServer时,出现如下图所示的错误: The server selected protocol version TLS10 is not accepted b ...
- C++ 使用MIDI库演奏《晴天》
那些在MIDI库里徘徊的十六分音符 终究没能拼成告白的主歌 我把周杰伦的<晴天>写成C++的类在每个midiEvent里埋藏故事的小黄花 调试器的断点比初恋更漫长而青春不过是一串未 ...
- BurpSuite重放发包的一些区别
目录 Send Group in parallel Single connection 和 Separate connections 的差别 实际应用场景 Reference 2022年之后,Burp ...
- 并发编程 - 线程同步(八)之自旋锁SpinLock
前面对互斥锁Monitor进行了详细学习,今天我们将继续学习,一种更轻量级的锁--自旋锁SpinLock. 在 C# 中,SpinLock是一个高效的自旋锁实现,用于提供一种轻量级的锁机制.SpinL ...
- HBuilder X对vue的支持有多强?
HBuilder X对vue的支持有多强? 分类:HBuilder Vue HBuilderX中使用vue,如果是打开vue文件,会自动挂载vue语法库.如果是HTML文件里引用vue框架,需要点 ...
- SpringBoot整合富文本编辑器(UEditor)
UEditro是一款比较好用的富文本编辑器,所谓的富文本编辑器就是和服务器交互的数据不是普通的字符串文件,而是一些内容包含比较广的字符串,一般是指的html页面,目前比较好用的是百度的UEditor, ...
- QT5笔记: 8. QT的全局变量(基本数据类型、函数、宏)
这个头文件中存放着QT类库的一些全局定义,一般Qt类的头文件会包含这个头文件: 基本数据类型 函数 宏 数据类型:保证跨平台时各数据类型长度统一 函数定义(一些数学函数未在QtGlobal中,而是在Q ...
- 2 本地部署DeepSeek模型构建本地知识库+联网搜索详细步骤
在1 使用ollama完成DeepSeek本地部署中使用ollama完成deepSeek的本地部署和运行,此时我可以在PowerShell中通过对话的方式与DeepSeek交流,但此时本地模型不具备联 ...
- Linux - Centos6/7忘记root密码怎么办?
转载:https://www.cnblogs.com/witz/p/10183533.html 一.Centos6.x (1)查看操作系统版本以及内核版本 (2)重启服务器,到如下界面 (3)好吧.. ...