跳台阶问题及其拓展延伸
问题描述
一共有n阶台阶,每次可以跳1阶或者2阶,假设从第0阶开始跳,问一共有多少种跳法?
问题分析:看到这个问题,我的第一反应就是使用递归。因为每次可以跳1阶或者2阶,那么,如何跳到第n阶呢,这就有两种方式,可以由第n-1阶跳到的第n阶,也可以由第n-2阶跳两步跳到第n阶;然后如何跳到第n-1阶呢,也有两种方式,同理对于如何跳到n-2阶,也是由两种方式;以此循环,采用递归的思路,很容易可以得到以下的代码
//*******************************************************//
//跳楼梯问题:每次可以跳1阶或者2阶,一共要跳n阶楼梯,
//求有多少种跳法?
//*******************************************************//
int Stare1(int n)//每次只能跳一阶或者两阶的楼梯
{
if(n>0)
{
if(n<3)
return n;
else
return Stare1(n-1)+Stare1(n-2);
}
else return 0;
}
问题延伸:每次可以跳1阶或者2阶,这个问题很简单,当每次可以跳[1,k]阶的时候呢,是不是问题就变得有点复杂了?有些同学就说,简单点,可以写成return Stare1(n-1)+Stare1(n-2)+...+Stare1(n-k);这不就行了,可是k是个变量的时候呢,这就没办法了吧!于是乎,第n阶跟其前面的n-1,n-2,...,n-k阶有关,所以我们建立一个数组,把前面每阶对应的跳法保存起来不就完了,所以得到以下代码
//*******************************************************//
//跳楼梯问题:每次可以跳[1,k]阶,一共要跳n阶楼梯,
//求有多少种跳法?
//*******************************************************//
int Stare2_1(int n,int k)//每次跳1到k阶
{
int *f = new int[n];//f用来存储[1,n]阶中每一阶对应的跳法
for(int i=0;i<n;i++)
{
f[i] = 0;
}
f[0]=1;
for(int i=1;i<k;i++)
{
for(int j=0;j<i;j++)
{
f[i] = f[i]+f[j];
}
f[i] = f[i]+1;
}
for(int i=k;i<n;i++)
{
for(int j=i-k;j<i;j++)
{
f[i] = f[i]+f[j];
}
}
for(int i=0;i<n;i++)
{
cout<<i+1<<", "<<f[i]<<endl;
}
return f[n-1];
}
当然了,当n很大的时候,k<<n,建立这个n维的数组,那岂不是很浪费空间,其实只要建立k维的一个数组就行了,于是乎又得到以下代码:
//*******************************************************//
//跳楼梯问题:每次可以跳[1,k]阶,一共要跳n阶楼梯,
//求有多少种跳法?
//Stare2_2是Stare2_1的另一种写法,Stare2_1要建立n维的数组
//Stare2_2只要建立k维的数组
//*******************************************************//
int Stare2_2(int n,int k)//每次跳1到k阶
{
int *f = new int[k];//f用来存储[1,n]阶中每一阶对应的跳法
for(int i=0;i<k;i++)
{
f[i] = 0;
}
f[0]=1;
for(int i=1;i<k;i++)
{
for(int j=0;j<i;j++)
{
f[i] = f[i]+f[j];
}
f[i] = f[i]+1;
}
for(int i=k;i<n;i++)
{
int sum=0;
for(int j=0;j<k;j++)
{
sum = sum+f[j];
}
for(int j=0;j<k-1;j++)
{
f[j] = f[j+1];
}
f[k-1]=sum;
}
if(n<k)
return f[n-1];
else
return f[k-1];
}
#include<iostream>
#include<math.h>
using namespace std;
.....
int main()
{
int n,k;//一共有n阶楼梯,每次可以跳[1,k]阶,一共有多少种跳法
cout<<"Please input two integer number n and k :";
cin>>n>>k;
cout<<"Stare1 : "<<endl;
for(int i=1;i<=n;i++)
{
cout<<i<<", "<<Stare1(i)<<endl;
}
cout<<n<<"阶台阶的跳法种数为:"<<Stare1(n)<<endl;
cout<<"Stare2_1 : "<<endl;
cout<<n<<"阶台阶的跳法种数为:"<<Stare2_1(n,k)<<endl;
cout<<"Stare2_2 : "<<endl;
for(int i=1;i<=n;i++)
{
cout<<i<<", "<<Stare2_2(i,k)<<endl;
}
cout<<n<<"阶台阶的跳法种数为:"<<Stare2_2(n,k)<<endl;
return 0;
}
当n=20,k=2的时候,得到的结果如下:



问题拓展:将一个正整数n,分解成2^n的求和形式,问一共有多少种分解形式?如5=4*1+1*1,5=2*2+1*1,5=2*1+1*3,5=1*5,一共有五种分解形式。
问题分析:咋一看,好像跟跳台阶问题有点相似,其实不然,跳台阶问题是个有序的问题,这是个无序的问题,在跳台阶问题中,5=1+2+2跟5=2+1+2是两种不同的方式,而这个分解问题中,是一种形式,这是最主要的区别!(当然了,也可以将跳台阶问题拓展到每次能够跳1,2,4,8,...,阶台阶,然后求n阶台阶的跳法?这个有待读者进行思考,下次我再更新)。针对这个问题,我们如何进行分析建模?假设n=8,那么可以这么分解:
8=8*1,
8=4*2,
8=4*1+2*2,
8=4*1+2*1+1*2,
8=4*1+1*4,
8=2*4,
8=2*3+1*2,
8=2*2+1*4,
8=2*1+1*6,
8=1*8,
一共有10种分解方法;
当n=11的时候,可以这么分解:
11=8*1+2*1+1*1,
11=8*1+1*3,
11=4*2+2*1+1*1,
11=4*2+1*3,
11=4*1+2*3+1*1,
11=4*1+2*2+1*3,
11=4*1+2*1+1*5,
11=4*1+1*7,
11=2*5+1*1,
11=2*4+1*3,
11=2*3+1*5,
11=2*2+1*7,
11=2*1+1*9,
11=1*11;
一种14中分解方法;
看了这两个例子,为什么我这么写,这么罗列出来,主要是给方便找出其中的分解规律。对于n=8,首先找出分解形式中,最大值为8的分解形式,再找出最大值为4的分解形式,接着找出最大值为2的分解形式,最后找出最大值为1的分解形式。最大值为8的,还要细分,根据含有几个8来细分;同理,根据含有几个4来细分最大值为4的;根据含有几个2来细分最大值为2的。这样子就能够找全所有的分解形式了!于是乎得到以下代码:
#include<iostream>
#include<math.h>
using namespace std;
const int M=2;//分解成M^0, M^1, M^2, ... 的形式
int Log(int n)//求以M为底的对数,并向0方向取整
{
int a = int(log(n*1.0)/log(M*1.0));
return a;
}
int Pow(int n)//求M^n
{
int a = int(pow(M*1.0,n*1.0));
return a;
}
//*******************************************************//
////整数分解问题,将整数n分解成1,2,4,8,16,...的组合的形式
//例如5=4+1,5=2*2+1,5=2*1+1*3,5=1*5
//*******************************************************//
int Stare3(int n,int k)
{
if(k<1 || n<=1)
{
return 1;
}
else
{
int sum=0;
for(int i=Log(n)>k?k:Log(n);i>0;i--)
{
for(int j=1;j<=n/Pow(i);j++)
{
sum = sum +Stare3(n-j*Pow(i),i-1);
}
}
sum++;
return sum;
}
}
int main()
{
//*******************************************************//
////整数分解问题,将整数n分解成1,2,4,8,16,...的组合的形式
//例如5=4+1,5=2*2+1,5=2*1+1*3,5=1*5
//*******************************************************//
for(int i=1;i<40;i++)
{
cout<<"n="<<i<<", "<<"k="<<5<<", sum="<<Stare3(i,Log(i))<<endl;
}
return 0;
}
实验结果如下图,前面n=1~10,本人已经验证无误,后面的因为数量比较大,还没有验证,大家可以加以验证,发下有错误的请跟我联系!


浙公网安备 33010602011771号