【递归+深搜+剪枝】详解+模板(不小心秒了三道题)

【递归+深搜+剪枝】详解+模板(不小心秒了三道题)

最近在开始恢复刷一些题,难度不是很大,康复训练了属于是。
今天偶然遇到三道类似的题,大概就是利用递归进行深度搜索,搜索的同时带一定的回溯剪枝来缩短搜索时长。ac掉第一道后,剩下两道稍微改了改也很神奇地秒掉了,所以想来记录一下,希望对大家有帮助。

Question1.跳台阶

一个楼梯共有 n 级台阶,每次可以走一级或者两级,问从第 0 级台阶走到第 n 级台阶一共有多少种方案。

输入格式

共一行,包含一个整数 n。

输出格式

共一行,包含一个整数,表示方案数。

数据范围

1≤n≤15

输入样例:
5
输出样例:
8
题解:

定义变量

cnt用来记录当前方案数
k代表当前已走的台阶数

搜索路径

走楼梯有1步\2步两种选择,因此有jump(n,k+1)和jump(n,k+2)两种递归方式

剪枝

若当前已走的台阶数>总共的台阶数(k>n),则剪枝,即return;

源代码
#include <iostream>
#include <cstring>
#include <algorithm>
using namespace std;
int cnt = 0;  //记录当前方案数
void jump(int n,int k)
{
    if(k==n) cnt++;//若已到达终点,cnt++
    else if(k>n) return;//若当前已走的台阶数>总共的台阶数,则剪枝
    else //两种搜索路径
    {
        jump(n,k+1);
        jump(n,k+2);
    }   
}
int main()
{
    int n;
    cin>>n;
    jump(n,0);//从起点开始搜索
    cout<<cnt<<endl;
    return 0;
}
Question2.走方格

给定一个 n×m 的方格阵,沿着方格的边线走,从左上角 (0,0) 开始,每次只能往右或者往下走一个单位距离,问走到右下角 (n,m) 一共有多少种不同的走法。

输入格式

共一行,包含两个整数 n 和 m。

输出格式

共一行,包含一个整数,表示走法数量。

数据范围

1≤n,m≤10

输入样例:
2 3
输出样例:
10
题解:

定义变量

cnt用来记录当前方案数,k代表当前所在的行数,p代表当前所在的列数

搜索路径

走放个有往右或往下两种选择,因此有 walk(n,m,k+1,p)和walk(n,m,k,p+1)两种递归方式

剪枝

若当前行或列走出边界值(k>n || p>m),则剪枝,即return;

源代码:
#include <iostream>
#include <cstring>
#include <algorithm>
using namespace std;
int cnt = 0;
void walk(int n,int m,int k,int p)//递归深度搜索
{ 
    if(k==n && p==m) cnt++; //若已到达终点,cnt++
    else if(k>n || p>m) return; //若当前行或列走出边界值(k>n || p>m),则剪枝
    else //两种搜索路径
    {
        walk(n,m,k+1,p);
        walk(n,m,k,p+1);
    }   
}
int main()
{
    int n,m;
    cin>>n>>m;
    walk(n,m,0,0); //从(0,0)点开始走
    cout<<cnt<<endl;
    return 0;
}
Question3.排列

给定一个整数 n,将数字 1∼n 排成一排,将会有很多种排列方法。

现在,请你按照字典序将所有的排列方法输出。

输入格式

共一行,包含一个整数 n。

输出格式

按字典序输出所有排列方案,每个方案占一行。

数据范围

1≤n≤9

输入样例:
3
输出样例:
1 2 3
1 3 2
2 1 3
2 3 1
3 1 2
3 2 1
题解:

定义变量

定义一个nums数组用于保存当前元素,is_visited数组用于记录当前元素是否被访问过,u记录当前已经存入nums的数的数量

搜索路径

只要is_visited为0即未被搜索过,就可以进行搜索,因此递归方式为Permutations(u+1,n,nums,is_visited);
注意:
递归之前要将 is_visited[i] 设为1,即在当前路径下设置已经访问过
并且递归过后一定要恢复现场!!
:将is_visited[i]再设为0,因为下次递归到这个数时为一条全新的路径

剪枝

因为nums定义时就只有n个,递归过程中也不存在超出范围的情况,本题无需剪枝。

源代码:
#include <iostream>
using namespace std;
void Permutations(int u,int n,int nums[],int is_visited[])//递归深度搜索
{
    if(u==n) //nums数组已满,到达终点,输出答案
    {
        for(int i =0;i<n;i++)
        {
            cout<<nums[i]<<" ";
        }
        cout<<endl;
    }
    else 
    {
        for(int i = 0;i<n;i++) //递归搜索时从头开始遍历
        {
            if(is_visited[i]==0) //未被访问过
                {
                    nums[u] = i+1; //存入当前数据
                    is_visited[i] = 1;//在当前路径下设置已经访问
                    Permutations(u+1,n,nums,is_visited);//递归搜索
                    is_visited[i] = 0;//恢复现场:为后面的路径设置未访问
                }
        }
    }   
}
int main()
{
    int n;
    cin>>n;
    int nums[n];
    int is_visited[n] = {0};
    Permutations(0,n,nums,is_visited);//从起点开始递归
    //cout<<ans<<endl;
    return 0;
}

最近才开始恢复刷一些题,没上很高的难度,有问题欢迎指正。

  • 3
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 2
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值