递推与递归/DFS

跳台阶

821. 跳台阶
在这里插入图片描述

这是一个典型的斐波那契数列 问题,我们可以通过分析递推关系来发现规律。

f(n) 表示从第 0 级台阶走到第 n 级台阶的方案数 。
通过列举发现:
当n=1,f(1)=1.
当n=2,f(2)=2
当n=3,f(3)=3=f(1)+f(2)
当n=4,f(4)=5=f(2)+f(3)

递推公式:f(n) = f(n - 1) + f(n - 2)

发现 f(n) 对应的数列就是斐波那契数列 ,只是需要有初始条件: f(0)=1f(1)=1
例如:

  • n = 2 时,f(2) = f(1) + f(0) = 1 + 1 = 2
  • n = 3 时,f(3) = f(2) + f(1) = 2 + 1 = 3
  • n = 4 时,f(4) = f(3) + f(2) = 3 + 2 = 5
  • n = 5 时,f(5) = f(4) + f(3) = 5 + 3 = 8(与题目样例一致 )

简单来说,从第 0 级到第 n 级台阶的方案数,符合斐波那契数列的规律,可通过上述递推关系计算任意 n(在题目数据范围 1 ≤ n ≤ 15 内 )对应的方案数。
递归数组求值:

void solve()
{
	int n;
	cin>>n;
	int f[n+10];
	f[0]=1,f[1]=1;
	for(int i=2;i<=n;i++)
	f[i]=f[i-1]+f[i-2];
	cout<<f[n]; 
}

递归函数,调用自身

int fibnaci(int x)
{
	if(x==1) return 1;
	if(x==2) return 2;
	else return fibnaci(x-1)+fibnaci(x-2);
}

递归实现指数型枚举

92. 递归实现指数型枚举
在这里插入图片描述
思路

每个数都有两种选择,选和不选,那么总共的方案数就是 2 n 2^n 2n种(一个数也不选也是一种方案)

递归、DFS最重要的是顺序
从1~n依次考虑每个数选或不选
在这里插入图片描述
以1这个数为例,用dfs向下搜索,分别两种情况,选或不选,直到搜到底,再向上回溯找到下一个点继续深搜。
总搜索过程见此图。
在这里插入图片描述
可以开一个数组记录选或不选,即状态数组。

#include<bits/stdc++.h>
using namespace std;
#define IOS ios::sync_with_stdio(0),cin.tie(0),cout.tie(0)
#define int long long
#define PII pair<int,int>
#define fi first
#define se second
#define endl '\n'
const int N=25;
int n,st[N];//st记录数字选还是没选的状态 
void dfs(int x)//x表示当前枚举到了哪个位置
{
	if(x>n)//结束条件 
	{
	for(int i=1;i<=n;i++)
	{
		if(st[i]==1)//如果选了 
		cout<<i<<" ";
	}	
	cout<<endl;
	return ;
	} 
	//不选 
	st[x]=2;
	dfs(x+1);
	st[x]=0;//恢复现场
	 //选 
	st[x]=1;
	dfs(x+1);//下一个位置
	st[x]=0;//恢复现场
} 

void solve()
{
	cin>>n;
	dfs(1);
}
signed main()
{
	IOS;
	int _=1;
//	cin>>_;
	while(_--)
	solve();
	return 0;
}


递归实现排列型枚举

P1706 全排列问题
在这里插入图片描述
这道题有个很简单的做法,直接用全排列函数(想了解的可以看这个博客:C++中的next_permutation全排列函数
全排列函数做法

#include<bits/stdc++.h>
using namespace std;
int main()
{
	int n,a[15];
	cin>>n;
	for(int i=1;i<=n;i++)
	a[i]=i;
	do{
		for(int i=1;i<=n;i++)
		printf("%5d",a[i]); 
		cout<<endl;
		}while(next_permutation(a+1,a+1+n));
	return 0;
}

这道题的要求是按照字典序的顺序,我们可以通过依次枚举每个位置可以放哪些数。。
在这里插入图片描述

#include<bits/stdc++.h>
using namespace std;
#define IOS ios::sync_with_stdio(0),cin.tie(0),cout.tie(0)
#define int long long
#define PII pair<int,int>
#define fi first
#define se second
#define endl '\n'
const int N=10;
int n,a[N];
bool st[N];//记录状态,这个位置空或不空 true为空,false为非空 
void dfs(int x)//传入的是当前位置 
{
	if(x>n)//终止条件 
	{
		for(int i=1;i<=n;i++)
		printf("%5lld",a[i]);//输出格式要注意 
		printf("\n");
		return ;
	}
	for(int i=1;i<=n;i++)
	{
		if(!st[i])//如果这个位置还没有选数 
		{
		st[i]=true;//标记 
		a[x]=i;//赋值给a数组 
		dfs(x+1);//下一个位置 
		//回溯,恢复现场 
		st[i]=false;
		a[x]=0;	
		}
	}
}
void solve()
{
	cin>>n;
	dfs(1);
}
signed main()
{
	IOS;
	int _=1;
//	cin>>_;
	while(_--)
	solve();
	return 0;
}


递归实现组合型枚举

P1157 组合的输出
在这里插入图片描述

和上面那题思路几乎一样,只不过上面那个是排列数,这个是组合数,要考虑数字越界,也就是选取数字时的顺序。所以dfs中还要传入一个枚举开始的数,这样当位置2枚举到5时就不会再继续往下枚举,x的值也不会大于r,自然就不会输出这个不合理的答案了

#include<bits/stdc++.h>
using namespace std;
#define IOS ios::sync_with_stdio(0),cin.tie(0),cout.tie(0)
#define int long long
#define PII pair<int,int>
#define fi first
#define se second
#define endl '\n'
const int N=28;
int n,r,a[N];
//因为这道题要求了顺序,也是要按顺序输出,因此要有开始第一个枚举的数
//比如当枚举到第二个位置填5的时候,后面就不能再进行下去 
void dfs(int x,int start)//一个是当前位置,一个是开始枚举的数 
{
	if(x>r)
	{
		for(int i=1;i<=r;i++)
		{
			printf("%3lld",a[i]);
		}
		printf("\n"); 
		return ;
	}  
	for(int i=start;i<=n;i++)
	{
		a[x]=i;
		dfs(x+1,i+1);
		a[x]=0; 
	}
} 
void solve()
{
	cin>>n>>r;
	dfs(1,1);
}
signed main()
{
	IOS;
	int _=1;
//	cin>>_;
	while(_--)
	solve();
	return 0;
}


P1036 选数

P1036 [NOIP 2002 普及组] 选数
在这里插入图片描述
这道题和上面那道非常相似,只是把数组换成了判断和是否为素数,同样是组合型的选择,我们要在dfs中传入当前位置和开始的起始位置,在x>k的时候进行剪枝,计算结果和,并判断结果是否为素数,并用ans记录素数的个数。

#include<bits/stdc++.h>
using namespace std;
#define IOS ios::sync_with_stdio(0),cin.tie(0),cout.tie(0)
#define int long long
#define PII pair<int,int>
#define fi first
#define se second
#define endl '\n'
//const int N=1e6+6;
int n,k,a[30],s[30],ans=0; 
int Prime(int num)//判断素数 
{
	if(num<2)
	return 0;
	int t=sqrt(num);
	for(int i=2;i<=t;i++)
	{
		if(num%i==0)
		return 0;
	}
	return 1;
}
void dfs(int x,int st)//分别传入当前位置和开始的起始位置 
{
	if(x>k)//停止条件 
	{
		int sum=0;
		for(int i=1;i<=k;i++)
			sum+=s[i];//相加判断和 
		if(Prime(sum))//如果是素数 
		ans++; //记录 
		return ;//剪枝 
	}
	for(int i=st;i<=n;i++)
	{
		s[x]=a[i];
		dfs(x+1,i+1); 
		s[x]=0;
	}
}
void solve()
{
	cin>>n>>k;
	for(int i=1;i<=n;i++)
	cin>>a[i];
	dfs(1,1);
	cout<<ans;
}
signed main()
{
	IOS;
	int _=1;
//	cin>>_;
	while(_--)
	solve();
	return 0;
}


关于DFS大约就这么几种情况,下期会出一个习题篇,敬请期待~

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值