每周学习总结: 第十一周(12).
本周学习: 1、递归算法 2、搜索(深度优先搜索 、广度优先搜索) 。
一:递归算法:
基本概念:( 程序直接或间接调用自身的编程技巧)
直接或间接调用自身的函数称为递归函数
它通常把一个大型复杂的问题层层转化为一个与原问题相似的规模较小的问题来求解。
基本思想: :层层分解。
把一个不能或不好解决的大问题转化为一个或几个小问题,再把这些小问题进一步分解成更小的小问题。
如4!=4* 3!;
3!=3* 2!;
2!=2*1!;
最小问题可以直接解决。
1!=1
算法关键:
1、找出递归定义。 2、找出递归终止条件。
所以由上述问题可解:
1、递归定义: n !=n*(n-1)!
2、递归终止条件: 1! =1
步骤:
1、分析问题、寻找递归:找出大规模问题与小规模问题之间的关系,这样通过递归使问题的规模逐渐变小。
2、设置边界、控制递归:找出停止条件。
3、设计函数、确定参数:设计函数体中的操作及相关参数。
二、搜索:
基本概念: 搜索算法是利用计算机的高性能来有目的地穷举一个问题的部分或所有的可能情况,从而求出问题的解的一种方法,相比于单纯的枚举有一定的方向性和目标性。
状态: 状态(state)是对问题在某一时刻进展情况的数学描述,或者是数学抽象。
状态转移: 每一个状态都会是答案的一个“可能的”解。状态的转移就是问题从一个状态转移到另一个状态,这样就可以进行搜索的一步步延伸,最后要得到的解也是其中的一个状态。
进行状态转移的方法:
1、深度优先搜索 (DFS) 2、广度优先搜索(BFS)
1、深度优先搜索 (DFS):
基本思想:
从初始状态,利用规则生成搜索树下一层任一个结点,检查是否出现目标状态,若未出现,以此状态利用规则生成再下一层任一个结点,再检查,重复过程一直到叶节点(即不能再生成新状态节点),当它仍不是目标状态时,回溯到上一层结果,取另一可能扩展搜索的分支。采用相同办法一直进行下去,直到找到目标状态为止。
按如下顺序:
状态必须在遍历完所有它的子状态之后,才能继续进行对同一层中下一个状态的遍历。
而且符合栈先进后出的性质。
(栈实现)具体过程:
1 每次取出栈顶元素,对其进行拓展。
2 若栈顶元素无法继续拓展,则将其从栈中弹出。继续1过程。
3 不断重复直到获得目标状态(取得可行解)或栈为空(无解)。
递归实现:
Function Dfs (Int Step, 当前状态)
{
Begin
{ 可加结束条件
从当前状态循环拓展下一个状态Next
If 状态Next合法 Then
Dfs (Step + 1, Next )
}
End
}
例题:
输入n,输出能整除数字n的数字串,该串只有0,1两个数字构成。
代码如下:
#include<iostream>
#include<stdio.h>
using namespace std;
int n;
int flag=0;
void dfs(unsigned long long s ,int step ){
if(step>20||flag)return;
if(s % n == 0){
cout<<s<<endl;
flag = 1;
return;
}
dfs(s*10 , step+1);
dfs(s*10+1 , step+1);
}
int main(){
while(scanf("%d",&n)){
if(n==0)break;
flag=0;
dfs(1, 1);
}
return 0;
}
2、广度优先搜索(BFS):
基本思想:从初始状态S 开始,利用规则,生成所有可能的状态。构成的下一层节点,检查是否出现目标状态G,若未出现,就对该层所有状态节点,分别顺序利用规则。
生成再下一层的所有状态节点,对这一层的所有状态节点检查是否出现G,若未出现,继续按上面思想生成再下一层的所有状态节点,这样一层一层往下展开。直到出现目标状态为止。
——在路径的寻找问题上用得比较多
按如下顺序:
广度优先即是要按层数一层一层来遍历,先将一层全部扩展,然后再进行下一层。
还可以利用队列先进先出(FIFO)的性质恰好可以来完成这个任务
对应的队列的情况:
(队列)具体过程:
1 每次取出队列首元素(初始状态),进行拓展
2 然后把拓展所得到的可行状态都放到队列里面
3 将初始状态删除
4 一直进行以上三步直到队列为空。
代码:
While Not Queue.Empty ()
{
Begin
可加结束条件
Tmp = Queue.Top ()
从Tmp循环拓展下一个状态Next
If 状态Next合法 Then
{
Begin
生成新状态Next
Next.Step = Tmp.Step + 1
Queue.Pushback (Next)
End
}
Queue.Pop ()
End
}
例题: 还是上述题 (用广度优先搜索方法。)
代码:
#include<iostream>
#include<stdio.h>
using namespace std;
long long bfs();
int n;
int main()
{
//int n;
while(cin>>n&&n)
cout<<bfs()<<endl;
return 0;
}
long long bfs()
{
queue<long long> q;
while(!q.empty()) q.pop();
q.push(1);
while(!q.empty())
{
if(q.front()%n==0) return q.front();
long long p=q.front();
q.pop();
q.push(p*10);
q.push(p*10+1);
}
return 0;
}
个人感想:
1、搜索适用范围:
下一个可能为终点的点可以由目前已经找到的点通过若干种方式递推得到,且递推的方式一般为有限个。
2、dfs、 bfs两个大致共同点:
一般需要一个数组记录访问过的点以避免重复搜索。
搜索的结构:
if(找到搜索终止条件){
结束搜索;
}
else{ //继续寻找可能的点
向各个方向继续搜索;
判断搜索到的点是否有意义:
//这里意义的由自己决定,首先肯定要保证搜索到的点在数据范围内,其次判断是否是一些题目要求的不能走的点(比如迷宫里的墙,当然还可以在这里进行剪枝
if(有意义)
继续搜索
//dfs表现为继续递归搜索,bfs表现为队列
}
3、有很多小细节,不熟练会有大问题!! 多做题。
下周目标:
1、每天 1~2 题 , 周末 2 ~3 题。
🆗