首页 > 其他分享 >2025dsfz集训Day3:DFS搜索与剪枝

2025dsfz集训Day3:DFS搜索与剪枝

时间:2025-01-19 09:24:22浏览次数:1  
标签:剪枝 洛谷 int Day3 DFS 搜索 dfs ans

DAY3: DFS搜索与剪枝

深搜

  • 深度优先搜索(DFS)是一种遍历或搜索树或图的算法,它从一个根节点开始,尽可能深地搜索每个分支,直到找到解为止。在搜索讨程中,为了提高效率,减少不必要的搜索,通常会采用各种剪枝优化策略。

剪枝基本思想

  • 在深度优先搜索中,我们通常会遍历图或树的所有节点和边,直到找到解或者遍历完所有节点。然而,某些情况下,我们知道某些路径不会导致有效的解,可以提前停止进一步的搜索,这就是剪枝。通过合理的剪枝策略,我们可以避免不必要的递归和计算极大地提高算法的效率

剪枝方法:

  • 最优化剪枝
    • 对应问题:求最优策略或最优答案类题目
    • 如果当前答案比之前记录的已有答案还差的话,就直接减掉。
  • 优化搜索顺序
    • 在一些搜索问题中,搜索树的各个层次、各个分支之间的顺序不是固定的。不同的搜索顺序会产生不同的搜索树形态,其规模大小也相差甚远。
    • 大部分情况下,我们应该优先搜索分支较少的节点,(说白了就是先搜小的分支,因为先搜大的浪费时间,详见P1223 排队接水)
  • 可行性剪枝
    • 在搜索过程中,及时对当前状态进行检查,如果发现分支已经无法到达递归边界,就执行回溯。
      (某些题目条件的范围限制是一个区间,此时可行性剪枝也被称为 "上下界剪枝"
  • 记忆化搜索
    • 可以记录每个状态的搜索结果,在重复调历一个状态时直接检索并返回。(就好像我们对图进行深度优先遍历时,标记一个节点是否已经被访问过。
  • 例题:

剪枝题目思路&代码

\(T1\)

#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\)

#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\)

  • 如果你看了讨论区,你会发现有一种做法,先加上所有猫的体重然后除以缆车的载重。但是很明显这样有时候猫就会被切开。

    所以正解是 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\)

#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\)

  • 我们先要解决一个问题:表面积怎么算?数学好的同学已经看出来了,就是每一层的侧面积(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

标签:剪枝,洛谷,int,Day3,DFS,搜索,dfs,ans
From: https://www.cnblogs.com/FrankWKD/p/18679204

相关文章

  • 深入HDFS——数据上传源码
    引入就如RPC篇章里提到的观点一样,任何一种能广为传播的技术,都是通过抽象和封装的思想,屏蔽底层底层复杂实现,提供简单且强大的工具,来降低使用门槛的。HDFS的风靡自然也是如此。通过前面深入了NameNode和DataNode的启动源码,我们已经是略有体会,但重启毕竟属于工作时几乎遇不到的......
  • 深入HDFS——DataNode启动源码
    引入上一篇我们看完了NameNode的启动源码,对于NameNode我们已经很熟悉了,今天我们接着来看看它的“得力干将”——DataNode。首先,自然还是从元数据管理篇提到的DataNode类(org.apache.hadoop.hdfs.server.datanode.DataNode)开始。不过在深入启动源码前,我们先看看它的源码注释:D......
  • hadoop--命令--hdfs fsck / -files -blocks--显示块信息
    [root@master~]#hdfsfsck/-files-blocksConnectingtonamenodeviahttp://master:50070/fsck?ugi=root&files=1&blocks=1&path=%2FFSCKstartedbyroot(auth:SIMPLE)from/192.168.94.128forpath/atSatJan1818:24:09CST2025//hbase/h......
  • java基础Day3 java语法
    java语法新建一个空项目,在项目中新建一个java模块文件菜单中打开项目结构,SDK有报红,要手动选,语言级别也要和SDK对应注释//单行注释/*多行注释*//**文档注释*@DescriptionHelloWorld*@Authortse121*/标识符关键字Demo01所有的标识符都应该以大小写字......
  • 一、Apache HDFS入门
    HDFS基本概念首先是一个==文件系统==,就是用来存储文件、存储数据。是大数据最底层一个服务。其次是一个==分布式的文件系统==。分布式意味着多台机器存储。场景互动:如何模拟实现分布式文件系统。或者说一个==成熟的分布式==文件系统应该要具备哪些属性、功能呢?1.分布式......
  • 树+dfs
    原题链接:https://codeforces.com/problemset/problem/2050/G题解链接:https://blog.csdn.net/Lazy_ChessPlayer/article/details/144279298#include<bits/stdc++.h>#definelcp<<1#definercp<<1|1#defineINF2e9usingnamespacestd;#definelowbit(x)......
  • 【详解】HadoopHDFS操作实例
    目录HadoopHDFS操作实例环境准备HDFS基本命令1.查看HDFS目录内容2.创建目录3.上传文件4.下载文件5.删除文件或目录6.查看文件内容高级操作1.文件重命名2.设置文件权限3.查看文件系统状态1.创建目录2.上传文件3.下载文件4.删除文件或目录注意事......
  • 十分钟写作Day3 1.15
    正文那真是令人心潮澎湃的一个时刻,听着墙上的钟表“哒,哒,哒……”地走着,我的内心不禁“扑通,扑通,扑通……”般跳动起来。我额头上出现的词语会是什么呢?也许是“外向”,毕竟我是妥妥的“E”人也许是“聪慧”,毕竟我这么聪明,从小就智商高也许是“合群”,毕竟我的交友圈极为广......
  • DFS 2025/1/15
    DFS&DFS剪枝优化Basic01 先搜节点少的分支如果搜进来一个大分支而答案不在此分支就会浪费大量时间02可行性剪枝已经白扯了就return03最优性剪枝如果此分支确定不是最优解(差于已有解)就return04记忆化搜索记录之前搜过Data,避免重复搜Question01[......
  • 代码随想录Day36 | 1049.最后一块石头的重量 II,494.目标和,474.一和零
    代码随想录Day36|1049.最后一块石头的重量II,494.目标和,474.一和零1049.最后一块石头的重量视为背包问题,求解sum/2容量背包能装下的最大重量返回的是这一部分石头与另一部分的差值的绝对值代码即为经典的01背包问题classSolution{publicintlastSt......