4.3 训练周记

本周博客回顾了深度优先搜索(DFS)和广度优先搜索(BFS)的基本概念,通过素数环实例演示了两者区别。重点讲解了DFS的剪枝和记忆化搜索,以及与动态规划(DP)的紧密联系。介绍了如何将DFS和BFS应用于背包问题的优化。后续内容概述未详述,适合搜索算法初学者和DP进阶者阅读。
摘要由CSDN通过智能技术生成

        这周主要回顾了基本的搜索算法。自己找了点时间重新刷了刷记忆化搜索,动态规划记录最优路径以及最长上升子序列及其优化。这次课上主要讲了bfs,dfs回溯的基础搜索算法,所以详细整理一下。

        搜索算法,本质就是有目的对解进行枚举,相比普通的枚举算法,可以限定搜索方向,避开本无意义的无效枚举。

首先是

dfs深度优先搜索

我课上印象比较深的例题那个素数环。

        如果没有学习dfs搜索算法,其实这题也能写,用for循环无目的的枚举每个位置该填的数,直到某个状态满足了素数环的定义,就输出。但是很明显这样的时间复杂度奇高达到了O(n^n)。

但是搜索可以简化这一操作,每次同样枚举当前该填的数,但是可以记录某个数是否被使用,如果被使用就没必要填他,而转向枚举下一个数。同时运用回溯的思想,可以让搜索在状态已经不合理的情况下回溯到上一状态。不必再继续枚举直到终点。

       从这里也可以看出,dfs的特点就是每次搜索按一条合法的特定线路搜索,直到不合法或者枚举完成才返回,直到遍历所有状态。期间我们可以通过判断调整dfs的方向。使其更多的避免无效搜索,加快搜索进程,这一操作也被称为剪枝。

与之相对的就是

bfs广度优先搜索

广度优先搜索如其名,就是每个步骤所有的状态全部搜索一遍,直到其中某个状态达到了搜索的目标,才停止。广搜常用队列实现,每次push一个父节点状态,并在遍历到这个父节点状态时将其所有可能的状态拓展,类似于大水漫灌。由此搜索的思路,我们可以知道,第一次搜索到的结果,必为最短操作数的结果。

从这两种搜索的思路来看,其实都有些暴力。dfs不到终点不回头,bfs在没达到目标的情况下,都要把所有可能都遍历一遍。所以基于这两个基本的搜索思路又衍生出了好多优化的算法,这块涉及到图了就不继续了。

但有一个与其联系比较紧密的,且又非常重要的算法或者说思维就是dp动态规划。

如dp中经典的01背包问题,如果用朴素的搜索来写,其实就是搜索每个物品拿不拿的状态,如果超过了背包的体积就回溯并在搜索完所有物品后取最优解返回。

dfs搜索思路大致如下


void dfs(int dep, int sum, int vleft) {//sum为当前的总价值,vleft为背包当前剩余体积
	if (vleft < 0) return;//如果容积不够还拿,就返回
	if (dep > n ||vleft==0) {
		if (sum > mx) mx = sum;
		return;
	}
	int vnext = vleft - v[dep];//vnext为拿了当前物品,对下一个物品决策的时候背包剩余体积
	dfs(dep + 1, sum + w[dep], vnext);//搜拿该物品的情况
	dfs(dep + 1, sum, vleft);//搜不拿该物品的情况
}

但是这样时间复杂度太高了,也太蠢了,每个可能都试一遍,直到不合法或者能拿的物品全拿完才返回。所有我们可以对他进行优化,比如有些情况下,明明之前尝试过了,但是遇到这种情况还是得再绕一次,大大加大了时间复杂度,所以我们可以采用记忆化的想法,记录每次搜索过的状态,如果搜索过这个状态直接返回结果即可。所以又引出了记忆化搜索,用一个rec数组记录搜索的状态。


int rec[10000][10000] = { 0 };
int  dfs(int dep ,int vnow) {//dep为搜索深度,也就是当前有几个物品
    int res=0;
    if (rec[dep][vnow] > 0) return rec[dep][vnow];//记忆数组已经记录过就输出
    if (dep == n) return  0;//搜索终点,物品都搜完了拿不了,返回0
    if (vnow - vb[dep] <0)
    res = dfs(dep + 1, vnow);        //拿不了的
    else if (vnow - vb[dep] >=0) 
    res= max(dfs(dep + 1, vnow), dfs(dep + 1, vnow - vb[dep]) + w[dep]);//取每个路线的最大值
    
    return rec[dep][vnow]=res;//记忆
}

但是如果状态数过多,数组开不下或者消耗内存太大了怎么办?所以想到将递归形式的搜索转换为迭代。


 
    for (int j = 1; j <= n; j++) {//物品数
        for (int i = v; i >= 1; i--) {//背包体积,从后往前逆向枚举
            //保证可以用到上一层外循环的dp值
            if (i - vb[j] >= 0)
                dp[i] = max(dp[i], dp[i - vb[j]] + w[j]);
        }
    }

这样一起整理,思路就清晰很多了。

本周还学习的一些重点因为篇幅过长,打算单独开一篇博客。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值