中阶算法---递归(3)

上台阶

  • 现在小瓜想通过台阶走上平台,最底层(小瓜所在的层)编号为1,最顶层编号为n。由于小瓜的腿比较短,他一次只能向上走1级或者2级台阶。小瓜想知道他有多少种方法走上平台,你能帮帮他吗?
    输入
    一个整数n,其中2≤n≤25。
    输出
    一行一个整数,表示小瓜上台阶的方案数
    数据范围
    对于12%的数据,2≤n≤5;
    对于28%的数据,2≤n≤10;
    对于100%的数据,2≤n≤25;
    输入样例
    4
    输出样例
    3
    样例解释
    从台阶1到台阶4,可能方案有:
    1→2→3→4,1→2→4,1→3→4,共3种。

这题我们首先想到的是递归。。。

//include<bits/stdc++.h>
#include <iostream>
#include<cstdio>
using namespace std;
int total;
int sum(int num,int n){
	if(num>n){
		return 0;
	}
	if(num==n){
		total++;
		return 0;
	}
	sum(num+1,n);
	sum(num+2,n);
}
int main(){
	int n;
	cin>>n;
	sum(1,n);
	cout<<total;
	return 0;
}

然后再思考除了递归,还有没有更高效的解决办法?
递归的毛病在于数据量一旦大了,就会发生时间超限的问题。
我们尝试一个档次高一点的方法,用在这里有点像斐波那契数列的赶脚。。。

//include<bits/stdc++.h>
#include <iostream>
using namespace std;
int f[55];
int main(){
	f[1]=1,f[2]=1;
	int n;
	cin>>n;
	for(int i=3;i<=n;i++){
		f[i]=f[i-1]+f[i-2];
	}
	cout<<f[n];
	return 0;
} 

沉思一会,发现这不就那个DP吗???这就是那个传说中巨难的动态规划??!
一点没错,这就是最简单基础的动态规划。。。

  • 小瓜想走上一个一共有n级的台阶,由于小瓜的腿长比较特殊,他一次只能向上走1级或者3级或者5级台阶。小瓜想知道他有多少种方法走上这n级台阶,你能帮帮他吗?
    输入
    一行一个整数n(n<=100000),表示一共有n级台阶。
    输出
    一行一个整数,表示小瓜上台阶的方案数对100003取余的结果。
    输入样例
    3
    输出样例
    2
    这题因为数据量变大了,只能用动态规划,可以参照上面的那个简单的dp做修改。。。
//include<bits/stdc++.h>
#include <iostream>
using namespace std;
const int N=100005;
int dp[N];
int total;
const int mod=100003;
int main(){
	int n;
	cin>>n;
	dp[0]=1,dp[1]=1,dp[2]=1;
	for(int i=3;i<=n;i++){
		if(n>=5){
			dp[i]=(dp[i-1]+dp[i-3]+dp[i-5])%mod;
		}else if(n>=3){
			dp[i]=(dp[i-1]+dp[i-3])%mod;
		}
	}
	cout<<dp[n];
	return 0;
}
  • 小瓜想走上一个一共有n级的台阶,由于小瓜的腿长比较特殊,他一次只能向上走1,4,7,10…(即3n+1)级台阶。小瓜想知道他有多少种方法走上这n级台阶,你能帮帮他吗?
    输入
    一行一个整数n(n<=100000),表示一共有n级台阶。
    输出
    一行一个整数,表示小瓜上台阶的方案数,结果对100003取余。
    输入样例
    5
    输出样例
    3
    样例解释
    对于样例,有3种走法:
    1,1,1,1,1
    1,4
    4,1

这题如果按照递归的思想来做的话:

//include<bits/stdc++.h>
#include <iostream>
#include<cstdio>
using namespace std;
int total,j;
int sum(int num,int n){
	if(num>n){
		return 0;
	}
	if(num==n){
		total++;
		total%=100003;
		return 0;
	}
	for(int i=0;i<=n;i++){
		j=3*i+1;
		sum(num+j,n);
	}
}
int main(){
	int n;
	cin>>n;
	sum(0,n);
	cout<<total;
	return 0;
}

虽然说思想上没什么错,但照题目所给的数据量来看,时间不够,会爆炸~
换成dp的思路重新分析一下:
同上台阶2的递推类似,假设我们有了 1−>(n−1) 的所有的答案,那么考虑到达位置 n 的最后一步可以跨 1,4,7,10… 级,对应从 n−1,n−4,n−7… 这些不同的位置直接到达 n ,且没有重复的方案,因此得到递推式为:
F(n)=F(n−1)+F(n−4)+F(n−7)+F(n−10)+…
这个算法的复杂度是 O(n2) 的。
我们对已有的递推式进行优化改进。
F(n)=F(n−1)+F(n−4)+F(n−7)+F(n−10)+…F(n−3)=F(n−4)+F(n−7)+F(n−10)+…
因此原来的递推式可以化简为:
F(n)=F(n−1)+F(n−3)
这个递推式的计算复杂度是 O(n) 的。

//include<bits/stdc++.h>
#include <iostream>
using namespace std;
const int N=100005;
int dp[N];
int total;
const int mod=100003;
int main(){
	int n;
	cin>>n;
	dp[0]=1,dp[1]=1,dp[2]=1;
	for(int i=3;i<=n;i++){
		dp[i]=(dp[i-1]+dp[i-3])%mod;
	}
	cout<<dp[n-1];
	return 0;
}

ps:至于这里的输出dp[n-1]而不是dp[n],是由于根据给出的样例分析得到的情况而做出的调整,或者根据递推式来得出的结论,总之动态规划还是一种比较玄的方法~

评论 4
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值