跳台阶
这是一个典型的斐波那契数列 问题,我们可以通过分析递推关系来发现规律。
设 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)=1
,f(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);
}
递归实现指数型枚举
每个数都有两种选择,选和不选,那么总共的方案数就是 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;
}
递归实现组合型枚举
和上面那题思路几乎一样,只不过上面那个是排列数,这个是组合数,要考虑数字越界,也就是选取数字时的顺序。所以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大约就这么几种情况,下期会出一个习题篇,敬请期待~