一个问题可以分解成若干子问题,且求解思路一样,当到一定的情况下有终止条件,这样的问题可以用递归方法求解
给你 n 个台阶,你的最大步幅是2步,可以一次走1步,也可以一次走2步,问有多少种走法?
以下所有代码原来采用 size_t 溢出,改用 unsigned long
#include <iostream>
using namespace std;
unsigned long cal(unsigned long n)
{
if(n==1)
return 1;
else if(n==2)
return 2;
return cal(n-1)+cal(n-2);
}
int main()
{
size_t n;
cout << "请输入你要走的台阶数 n :" ;
cin >> n;
cout << "走台阶有 " << cal(n) << " 种方案。" << endl;
return 0;
}
以上递归方法,在 n 比较小的时候运行时间较短 输入 n = 100 时,超过10s还没出结果,我就终止程序了。以下改用循环。
#include <iostream>
using namespace std;
int main() //循环
{
unsigned long n, step, nextStep = 2, nextnextStep = 1;
cout << "请输入你要走的台阶数 n :" ;
cin >> n;
if(n > 0)
{
if(n == 1)
{
step = 1;
} else if(n == 2)
{
step = 2;
}
else
{
for(int i = 2; i < n; ++i)
{
step = nextStep + nextnextStep;
nextnextStep = nextStep;
nextStep = step;
}
}
cout << "走台阶有 " << step << " 种方案。" << endl;
}
return 0;
}
输入 n = 100 时,改用循环,眨眼间出结果。
代码 1 中的 f(n), 比如 n = 5 时
以下代码,屏蔽多次计算重复的值
#include <iostream>
#include <iterator>
#include <map>
using namespace std;
unsigned long cal(unsigned long n, map<unsigned long, unsigned long>& n_fn_map)
{
map<unsigned long,unsigned long>::iterator iter = n_fn_map.find(n); //查找key n
if(iter != n_fn_map.end())
{
return iter->second; //如果找到了,就不必进行下面计算了,直接返回value
}
if(n==1)
{
n_fn_map.insert(pair<unsigned long, unsigned long>(1,1)); //把f(1)存入映射
return 1;
}
else if(n==2)
{
n_fn_map.insert(pair<unsigned long, unsigned long>(2,2)); //把f(2)存入映射
return 2;
}
else
{
size_t sum = cal(n-1,n_fn_map)+cal(n-2,n_fn_map); //递归调用函数
n_fn_map.insert(pair<unsigned long, unsigned long>(n,sum)); //求得的f(n)存入映射,供后面查询直接使用
return sum;
}
}
int main() //递归(带避免重复计算fn的值功能)
{
size_t n;
cout << "请输入你要走的台阶数 n :" ;
cin >> n;
map<unsigned long, unsigned long> n_Fn; //n,f(n)的 k,v 容器
cout << "走台阶有 " << cal(n,n_Fn) << " 种方案。" << endl;
return 0;
}
输入 n = 100,程序也是眨眼间出结果
测试程序运行时间shell代码:https://blog.csdn.net/qq_21201267/article/details/81840299
给你 n 个台阶,你的最大步幅是2步,可以一次走1步,也可以一次走2步,先迈左脚,要求最后到达时是右脚,问有多少种走法?
解法1:模拟实际的行走,暴力搜索
/**
1. @description: 39个台阶,一次走1步或2步,左脚出发,要求右脚到达
2. @author: michael ming
3. @date: 2019/4/6 18:17
4. @modified by:
*/
#include <iostream>
using namespace std;
void recursion(const unsigned long &targetStairs, unsigned long steps, unsigned long stairsWalkAway, unsigned long &ways)
{ //暴力搜索,n很大时效果不好
if(stairsWalkAway > targetStairs) //走过了,不做记录
return;
else if(stairsWalkAway == targetStairs && steps%2 == 0) //正好走到,且步数为偶数(右脚到达)
{
ways++; //记录一种方案可以
return;
}
else //没走到,继续递归
{
recursion(targetStairs, steps+1, stairsWalkAway+1, ways);
recursion(targetStairs, steps+1, stairsWalkAway+2, ways);
}
}
int main()
{
unsigned long stairs = 0, steps = 0, stairsWalkAway = 0, ways = 0;
cout << "请输入台阶个数:" << endl;
cin >> stairs;
recursion(stairs, steps, stairsWalkAway, ways);
cout << "左脚出发,右脚到达的方案有:" << ways << " 种。" << endl;
return 0;
}
解法2:递推公式和之前一样,结束条件变了
#include <iostream>
#include <map>
using namespace std;
unsigned long cal(size_t n, size_t stepWalkAway)
{
if(n==1)
{
if(stepWalkAway%2 == 0)
return 0; //只剩1步了,如果走过了偶数步,那就是右脚达到,不可能了,0
return 1;
}
else if(n==2) //n = 2 时,不论什么情况,大家都只有1种可能,使得右脚到达
{
return 1;
}
else
{
return cal(n-1,stepWalkAway+1)+cal(n-2,stepWalkAway+1); //递归调用函数
}
}
int main()
{
size_t n, stepWalkAway = 0;
cout << "请输入你要走的台阶数 n :" ;
cin >> n;
cout << "左脚开走,右脚走到有 " << cal(n,stepWalkAway) << " 种方案。" << endl;
return 0;
}
解法3:动态规划,现在还不太明白 见他人博客: https://blog.csdn.net/qq_40269087/article/details/80236102 大概的思路是:自底向上
#include <iostream>
using namespace std;
unsigned long dynamicProgram(size_t N)
{
unsigned long left[N+1], right[N+1];
left [1] = 1; left [2] = 1; right [1] = 0; right [2] = 1;
for(size_t i = 3; i <= N; ++i)
{
left[i] = right[i-1] + right[i-2];
right[i] = left[i-1] + left[i-2];
}
return right[N]; //题目要求返回右脚到达方案
}
int main()
{
size_t N;
cout << "请输入你要走的台阶数 n :" ;
cin >> N;
cout << "左脚开走,右脚走到有 " << dynamicProgram(N) << " 种方案。" << endl;
return 0;
}