【递归+深搜+剪枝】详解+模板(不小心秒了三道题)
最近在开始恢复刷一些题,难度不是很大,康复训练了属于是。
今天偶然遇到三道类似的题,大概就是利用递归进行深度搜索,搜索的同时带一定的回溯剪枝来缩短搜索时长。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;
}
最近才开始恢复刷一些题,没上很高的难度,有问题欢迎指正。