递归的思考
本菜鸡终于开始学算法了,最近遇到递归的问题感觉难以理解,在这里写一些作题的错处与相关的总结
**递归的总体思维:**
1.找到停止递归的条件
2.找到每一层递归之间的联系
> 问题1:**将多层循环简化**
> 找出从自然数1 、 2、……、m中任取k 个数的所有组合。 例如m=5,k=3
> 案例:输入5 3 的时候
> 5 3
5 4 3
5 4 2
5 4 1
5 3 2
5 3 1
5 2 1
4 3 2
4 3 1
4 2 1
3 2 1
分析:
这个问题如果暴力枚举的话,是要用多重循环的,每一列对应一层循环,但是复杂度很大——————>简化:其实每一层的循环都是**相似处理方法**的循环,**每一层之间也有一定的联系**。
在这个问题中:
停止递归的条件是:当已经找到了k个数字的时候(这一步可以用计数器来进行记录)
每一层之间的关系: 第一个数确定的时候,后面的数确定就转化为从k-1个数里找m-1个数的组合----------------->这是递归所体现出来的问题规模缩小
每一层的输出:*当计数器计数到k的时候,终会输出,我们需要输出的数据:之前前面几层留存的数据,但是在递归过程中难以传递,——————》可以另外设置一个数组,做记忆的作用
#include <iostream>
typedef long long ll;
using namespace std;
int n,k,ans[100];//把第cur个数字储存起来
void print()
{
for(int i=1;i<=k;i++)
cout<<ans[i]<<' ';
cout<<endl;
}
void dfs(int x,int cur)/*ra表示范围是1->x,cur表示他是第几个数*/
{
int i;
if(cur==k+1) //在cur更新到k的时候循环相应次数并且输出
print();
for(i=x;i>=1;i--) /*每次进入循环总是递归到最小的一位开始输出*/
{
ans[cur]=i;
dfs(i-1,cur+1); //每次进入一个不是末位的数字都要进行一轮循环
}
}
此处我自己对这个地方的递归算法一开始想不通的
1.为什么递归函数入口放在for循环里面----
在思路里提到过,其实这是一个多重循环,当需要进入递归入口,也就意味着还没到最里层的循环(最里层的函数循环结束之后,就会处处相应的结果啦!!),所以在for里面放递归函数,意味着 每进入一个新的递归函数,就进入了一层新的循环(这也是为什么结束条件是循环到第k数的时候)
2.当一次递归走到结束之后,发生什么?
由于递归函数是在for循环里面的,进入递归函数意味着在这一层函数的这一个i,这一次执行的过程中发生递归,(如果条件允许他可能这一层的每个i都会进入递归)
当一层递归走到结束的时候,会返回到上一层循环,并且继续循环
2.整数分解
输出一个正整数n的所有整数和形式。如n=4
3
3=3
3=2+1
3=1+2
3=1+1+1
分析:这道题是上一道题的一个升级,各层之间的关系与其递归的思想体现在:确定第一个数i之后,剩下的问题就是把n-i分解(问题规模缩小)
此处要停止递归的条件:没东西可以分了,rest==0;
#include <iostream>
typedef long long ll;
using namespace std;
int n,rest,bit,store[100];
void print()
{
int i=0;
cout<<n<<'=';
while(store[i]!=0)
{
cout<<store[i];
if(store[i+1]!=0)
cout<<'+';
i++;
}
cout<<endl;
}
//每一位就是一个拆分的循环
void dfs(int rest,int bit) //由于是整数拆分,每次进入递归循环应该都是用rest
{
int i;
for(i=rest;i>=1;i--)
{
store[bit]=i; //储存该位
if(rest-i==0) //先写结束条件,单层循环里面(for)rest的值不会更新,所以一点过
{store[i+1]=0; //设置输出断点
print();
else dfs(rest-i,bit+1);//更新rest和bit的值,不要写成bit++的形式,只是传入值的副本变了
}
// return 0;
}
int main()
{
ios::sync_with_stdio(0),cin.tie(0);
cin>>n;
rest=n;
dfs(n,0);
return 0;
}
出现的问题:
这个代码写了超级久(T_T)
问题1:使用了if(rest==0)为结束条件,错误想法:没东西可分,也就是rest==0,我的输出函数使用的是while循环,即使此处不结束,在调用dfs之后rest为0也会结束
纠正:注意for循环的进入条件:注意递归函数进入条件和输出条件都在for循环里,rest==0,根本无法进入for循环,且多进一层递归函数会导致空间的浪费,记录一位就要检验一次rest还能不能分解
问题2:在递归函数调用的时候采用了bit++这样的形式传递数据,在一次最内层函数结束之后,跳转到倒数第二层的数组储存位置会出问题,本来应该从0开始,但递归被调了多少次,就离谱多少步
纠正:函数传递的是数据的副本,使用bit++或者++bit都会使得bit的值产生实际的变化
问题3:一开始没有设置断点,有些数字储存比较远但并没有被覆盖,在输出的时候会产生错
解决:1.增加断点
2.输出的时候获得应输出的长度(bit)用for循环控制输出
问题升级!!:不能输出重复的,比如4=3+1之后不可以输出4=1+3
问题分析:在进入新一轮的循环中,我们要比上一轮确定的数字相等或者小
代码改写:
void dfs(int pre,int rest,int bit) //由于是整数拆分,每次进入递归循环应该都是用rest
{
int i;
for(i=pre;i>=1;i--) //向下传递一个目前函数状态的参数
{
if(rest-i<0) continue; //要保证rest-i不小于0,只有i很大的时候才会<0,所以coutine可以使得i不断减小,直到合适的值
store[bit]=i;
//if(bit>0&&store[bit]>store[bit-1]) continue;
if(rest-i==0) //先写结束条件,单层循环里面(for)rest的值不会更新,所以一点过
store[bit+1]=0,
print();
else dfs(i,rest-i,bit+1);//更新rest和bit的值,不要写成bit++的形式,只是传入值的副本变了
}