算法基础--递归

img

😀前言
递归是一种重要的算法思想,常用于解决问题的分解与求解。在计算机科学中,递归是指一个函数在其定义中调用自身的情况。

🏠个人主页:尘觉主页

算法基础–递归

递归

递归算法在计算机系统中用栈帮助实现,一般常见的算法有深度优先遍历(DFS),可以解决的问题有迷宫问题是否连通的问题,递推会对应一个递归搜索树,递归搜索树可以帮助我们更好的理解递归的流程,递归要注意的有是否可以进行剪枝,在迷宫问题中,也要考虑是否要保存原有的迷宫。

入门例题

递归实现指数型枚举

从 1∼n 这 n 个整数中随机选取任意多个,输出所有可能的选择方案。

输入格式
输入一个整数 n。

输出格式
每行输出一种方案。

同一行内的数必须升序排列,相邻两个数用恰好 1 个空格隔开。

对于没有选任何数的方案,输出空行。

本题有自定义校验器(SPJ),各行(不同方案)之间的顺序任意。

数据范围
1≤n≤15
输入样例:
3
输出样例:

3
2
2 3
1
1 3
1 2
1 2 3

题解:

对于指数型枚举一个数只有选与不选的区分,所以我们从第一个位置,枚举到第n个位置,在第i个位置上,i这个数只有选与不选的区别,选的话我们将st[i]记录为i;不选记录为-1;一直到u>n时枚举了所有的位置,此时输出即可,要注意的是在输出完后要记得return掉

#include<cstdio>
#include<cstring>
#include<iostream>
#include<algorithm>

using namespace std;

const int N=20;
int n;
int st[N];
void dfs(int u){
    if(u>n){
        for(int i=1;i<=n;i++){
            if(st[i]==1) cout<<i<<" ";
        }
        cout<<endl;
        return ;
    }
    
    st[u]=1;
    dfs(u+1);
    
    st[u]=-1;
    dfs(u+1);
}
int main(){
    cin>>n;
    dfs(1);
    return 0;
}
递归实现排列型枚举

把 1∼n 这 n 个整数排成一行后随机打乱顺序,输出所有可能的次序。

输入格式
一个整数 n。

输出格式
按照从小到大的顺序输出所有方案,每行 1 个。

首先,同一行相邻两个数用一个空格隔开。

其次,对于两个不同的行,对应下标的数一一比较,字典序较小的排在前面。

数据范围
1≤n≤9
输入样例:
3
输出样例:
1 2 3
1 3 2
2 1 3
2 3 1
3 1 2
3 2 1

题解

在排列型枚举中,我们有n个位置,在每个位置上分别枚举这个位置可以放那个数,所以我们有一个path数组来记录排列的方案,使用st的bool数组来判断这个数是否选过。

#include<cstdio>
#include<cstring>
#include<iostream>
#include<algorithm>

using namespace std;

const int N=15;
int n;
bool st[N];
int path[N];
void dfs(int u){
    if(u>n){//所有位置枚举完成
        for(int i=1;i<=n;i++){
            cout<<path[i]<<" ";
        }
        cout<<endl;
        return ;
    }
    
    for(int i=1;i<=n;i++){//在第u个位置上枚举所有方案,这个位置上可以放置所有没有被用过的数字。
        if(!st[i]){
            path[u]=i;
            st[i]=true;//表示这个数被用过了
            dfs(u+1);
            st[i]=false;//还原状态,保证回溯时下一层递归一致。
        }
    }
}
int main(){
    cin>>n;
    dfs(1);
    
    return 0;
}
递归实现组合型枚举

从 1∼n 这 n 个整数中随机选出 m 个,输出所有可能的选择方案。

输入格式
两个整数 n,m ,在同一行用空格隔开。

输出格式
按照从小到大的顺序输出所有方案,每行 1 个。

首先,同一行内的数升序排列,相邻两个数用一个空格隔开。

其次,对于两个不同的行,对应下标的数一一比较,字典序较小的排在前面(例如 1 3 5 7 排在 1 3 6 8 前面)。

数据范围
n>0 ,
0≤m≤n ,
n+(n−m)≤25
输入样例:
5 3
输出样例:
1 2 3
1 2 4
1 2 5
1 3 4
1 3 5
1 4 5
2 3 4
2 3 5
2 4 5
3 4 5

题解

在组合数枚举中,我们可以通过认为确定枚举的顺序来通过类似排列数的方法来实现,不同的一点时在排列数枚举时,我们要在传一个参数num表示前一位枚举到那个数字,首先写一个朴素方法,该方法的时间是1601ms

#include<cstdio>
#include<cstring>
#include<iostream>
#include<algorithm>

using namespace std;

const int N=25;
int n,m;
int path[N];
bool st[N];
void dfs(int u,int num){
    if(u>m){
        for(int i=1;i<=m;i++){
            cout<<path[i]<<" ";
        }
        cout<<endl;
        return;
    }
    
    
    for(int i=1;i<=n;i++){
        if(!st[i]&&i>num){
            st[i]=true;
            path[u]=i;
            dfs(u+1,i);
            st[i]=false;
        }
    }
}
int main(){
    cin>>n>>m;
    dfs(1,0);
    
    return 0;
}

下面做一个优化

我们在递归前提前判断一个,上一个位置的数是否合理,如果后面剩的数字不能满足m个位置和递增的条件就直接return掉,进行剪枝,优化时间复杂度。该方法的时间是103ms

#include<cstdio>
#include<cstring>
#include<iostream>
#include<algorithm>

using namespace std;

const int N=25;
int n,m;
int path[N];
bool st[N];
void dfs(int u,int num){
    if(num>n-m+u-1)return;
    if(u>m){
        for(int i=1;i<=m;i++){
            cout<<path[i]<<" ";
        }
        cout<<endl;
        return;
    }
    
    
    for(int i=1;i<=n;i++){
        if(!st[i]&&i>num){
            st[i]=true;
            path[u]=i;
            dfs(u+1,i);
            st[i]=false;
        }
    }
}
int main(){
    cin>>n>>m;
    dfs(1,0);
    
    return 0;
}

迷宫问题

通过深度优先搜索(DFS)方法实现。

迷宫问题一

一天蒜头君掉进了一个迷宫里面,蒜头君想逃出去,可怜的蒜头君连迷宫是否有能逃出去的路都不知道。

看在蒜头君这么可怜的份上,就请聪明的你告诉蒜头君是否有可以逃出去的路。

输入格式
第一行输入两个整数 nn 和 mm,表示这是一个 n \times mn×m 的迷宫。

接下来的输入一个 nn 行 mm 列的迷宫。其中 ‘S’ 表示蒜头君的位置,'*‘表示墙,蒜头君无法通过,’.‘表示路,蒜头君可以通过’.'移动,'T’表示迷宫的出口(蒜头君每次只能移动到四个与他相邻的位置——上,下,左,右)。

输出格式
输出一个字符串,如果蒜头君可以逃出迷宫输出"yes",否则输出"no"。

数据范围
1 \le n, m \le 101≤n,m≤10。

输出时每行末尾的多余空格,不影响答案正确性

样例输入1复制
3 4
S**.
…*.
*T
样例输出1复制
no
样例输入2复制
3 4
S
.

***T
样例输出2复制
yes

题解

我们读入所有数据,然后获得起点S的坐标。然后深度优先遍历,在迷宫问题中进入DFS后,要先判断是否到中点,在判断是否是障碍物,然后标记该点访问过了。

#include<cstdio>
#include<cstring>
#include<iostream>
#include<algorithm>

using namespace std;
const int N=15;
int n,m;
char g[N][N];
int dx[4]={-1,0,1,0},dy[4]={0,1,0,-1};
bool st[N][N];
bool dfs(int x,int y){
    if(g[x][y]=='T'){
        return true;
    }
    if(g[x][y]=='*') return false;
    st[x][y]=true;
    for(int i=0;i<4;i++){
        int a=x+dx[i],b=y+dy[i];
        if(a>n||a<=0||b<=0||b>m)continue;
        if(st[a][b])continue;
        if(dfs(a,b)){
            return true;
        }
    }
    return false;
}
int main(){
    cin>>n>>m;
    int x,y;
    for(int i=1;i<=n;i++){
        for(int j=1;j<=m;j++){
            cin>>g[i][j];
            if(g[i][j]=='S'){
                x=i;
                y=j;
            }
        }
    }
    if(dfs(x,y)){
        cout<<"yes"<<endl;
    }else{
        cout<<"no"<<endl;
    }
    
    return 0;
}
迷宫问题二

蒜头君在你的帮助下终于逃出了迷宫,但是蒜头君并没有沉浸于喜悦之中,而是很快的又陷入了思考,从这个迷宫逃出的最少步数是多少呢?

输入格式
第一行输入两个整数 nn 和 mm,表示这是一个 n \times mn×m 的迷宫。

接下来的输入一个 nn 行 mm 列的迷宫。其中 ‘S’ 表示蒜头君的位置,'*‘表示墙,蒜头君无法通过,’.‘表示路,蒜头君可以通过’.'移动,'T’表示迷宫的出口(蒜头君每次只能移动到四个与他相邻的位置——上,下,左,右)。

输出格式
输出整数,表示蒜头君逃出迷宫的最少步数,如果蒜头君无法逃出迷宫输出 -1−1。

数据范围
1 \le n, m \le 101≤n,m≤10。

输出时每行末尾的多余空格,不影响答案正确性

样例输入1复制
3 4
S**.
…*.
*T
样例输出1复制
-1
样例输入2复制
3 4
S
.

***T
样例输出2复制
5

题解

本题要求判断是否可以到达并且要计算出最短路径,其实用宽度优先搜索更为合适,因为宽度优先搜索第一次到达目的地就是最短路径,但是我们使用深度优先也可以实现,我们定义一个最短量来储存最短的路径,当每一次到达目的点就比较一下与最短路的大小,交换最短路径长度,因此我们要遍历所有的可行路径,所以就要回溯访问状态,所以在一个遍历后就要复原,将一个点置为未访问,额额额,在 这道题中,我开始忘了读入n和m所以出现了segment段错误,还检查了好久没查到。

#include<cstdio>
#include<cstring>
#include<iostream>
#include<algorithm>

using namespace std;
const int N=15;
char g[N][N];
bool st[N][N];
int Min=99999;
int m,n;
int dx[4]={-1,0,1,0},dy[4]={0,1,0,-1};
void dfs(int x,int y,int stmp){
    if(stmp>Min) return ;
    if(g[x][y]=='T'){
        Min=min(Min,stmp);
        return;
    }  
    st[x][y]=true;
    if(g[x][y]=='*') return ;
    for(int i=0;i<4;i++){
        int a=x+dx[i],b=y+dy[i];
        if(a>n||a<=0||b>m||b<=0) continue;
        if(st[a][b]) continue;
        if(g[a][b]=='*') continue;
        dfs(a,b,stmp+1);
        st[a][b]=false;
        
    }
}
int main(){
    cin>>n>>m;
    int a,b;
    for(int i=1;i<=n;i++){
        for(int j=1;j<=m;j++){
            cin>>g[i][j];
            if(g[i][j]=='S'){
                a=i,b=j;
            }
        }
    }
    dfs(a,b,0);
    if(Min==99999){
        cout<<-1<<endl;
    }else{
        cout<<Min<<endl;
    }
    return 0;
}
迷宫问题三

经过思考蒜头君终于解决了怎么计算一个迷宫的最短路问题,于是蒜头君找到一个新的迷宫图,来验证自己是否真的会计算一个迷宫的最短路。

为了检验自己计算的是否正确,蒜头君特邀你一起来计算。

输入格式
第一行输入两个整数 nn 和 mm,表示这是一个 n \times mn×m 的迷宫。

接下来的输入一个 nn 行 mm 列的迷宫。其中’@‘表示蒜头君的位置,’#‘表示墙,蒜头君无法通过,’.‘表示路,蒜头君可以通过’.‘移动,所有在迷宫最外围的’.'都表示迷宫的出口(蒜头君每次只能移动到四个与他相邻的位置——上,下,左,右)。

输出格式
输出整数,表示蒜头君逃出迷宫的最少步数,如果蒜头君无法逃出迷宫输出 -1−1。

数据范围
1 \le n,m \le 151≤n,m≤15。

输出时每行末尾的多余空格,不影响答案正确性

样例输入1复制
9 13
#############
#@…#
#####.#.#.#.#
#…#
#.#.#.#.#.#.#
#.#…#.#
#.#.#.#.#.#.#
#…#
#####.#######
样例输出1复制
11
样例输入2复制
4 6
#.####
#.#.##
#…@#

样例输出2复制
5

题解

该迷宫问题与第二个迷宫问题类似,我们也要求出最短路径,所以一样要使用minn记录短的路径。

但是这个题要注意到达的条件,和第二个迷宫终点判断不一样,这个题要观察迷宫的构造,判断终止条件。所以这道题尽量从1开始存储迷宫图。

#include<cstdio>
#include<cstring>
#include<iostream>
#include<algorithm>

using namespace std;
const int N=20;
int n,m;
int dx[4]={-1,0,1,0},dy[4]={0,1,0,-1};
char g[N][N];
bool st[N][N];
int minn=99999;
void dfs(int x,int y,int stmp){
    if(stmp>minn)return ;
    if(x==0||y==0||x==n+1||y==m+1){
        minn=min(minn,stmp);
        return ;
    }
    st[x][y]=true;
    for(int i=0;i<4;i++){
        int a=x+dx[i],b=y+dy[i];
        if(g[a][b]=='#')continue;
        if(st[a][b])continue;
        dfs(a,b,stmp+1);
        st[a][b]=false;
    }

}
int main(){
    cin>>n>>m;
    int x,y;
    for(int i=1;i<=n;i++){
        for(int j=1;j<=m;j++){
            cin>>g[i][j];
            if(g[i][j]=='@'){
                x=i;y=j;
            }
        }
    }
    dfs(x,y,0);
    if(minn==99999){
        cout<<-1<<endl;
    }else{
        cout<<minn-1<<endl;
    }
    return 0;
}

好了,dfs递归就先写到这里

😁热门专栏推荐
想学习vue的可以看看这个

java基础合集

数据库合集

redis合集

nginx合集

linux合集

手写机制

微服务组件

spring_尘觉

springMVC

mybits

等等等还有许多优秀的合集在主页等着大家的光顾感谢大家的支持

🤔欢迎大家加入我的社区 尘觉社区

文章到这里就结束了,如果有什么疑问的地方请指出,诸佬们一起来评论区一起讨论😁
希望能和诸佬们一起努力,今后我们一起观看感谢您的阅读🍻
如果帮助到您不妨3连支持一下,创造不易您们的支持是我的动力🤞

img

相关推荐

  1. ---算法

    2024-04-04 04:32:03       41 阅读

最近更新

  1. docker php8.1+nginx base 镜像 dockerfile 配置

    2024-04-04 04:32:03       75 阅读
  2. Could not load dynamic library ‘cudart64_100.dll‘

    2024-04-04 04:32:03       80 阅读
  3. 在Django里面运行非项目文件

    2024-04-04 04:32:03       64 阅读
  4. Python语言-面向对象

    2024-04-04 04:32:03       75 阅读

热门阅读

  1. 4/4 清明work

    2024-04-04 04:32:03       32 阅读
  2. 什么是设计模式?使用英雄联盟来介绍设计模式

    2024-04-04 04:32:03       35 阅读
  3. “八皇后”问题——回溯+深搜

    2024-04-04 04:32:03       32 阅读
  4. 程序员的前景和未来

    2024-04-04 04:32:03       36 阅读
  5. 关系数据库标准语言SQL难题整理

    2024-04-04 04:32:03       32 阅读
  6. C语言--指针4

    2024-04-04 04:32:03       33 阅读