跳台阶问题及其拓展延伸

问题描述

一共有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,本人已经验证无误,后面的因为数量比较大,还没有验证,大家可以加以验证,发下有错误的请跟我联系!

posted @ 2015-07-02 01:32  LUOENHU  阅读(524)  评论(0)    收藏  举报