其他分享
首页 > 其他分享> > 洛谷——P1028 数的计算

洛谷——P1028 数的计算

作者:互联网

题目描述

我们要求找出具有下列性质数的个数(包含输入的自然数nnn):

先输入一个自然数nnn(n1000n\leq1000n≤1000),然后对此自然数按照如下方法进行处理:

  1. 不作任何处理;

  2. 在它的左边加上一个自然数,但该自然数不能超过原数的一半;

  3. 加上数后,继续按此规则进行处理,直到不能再加自然数为止.

输入格式

自然数nnn,且n1000n\leq1000n≤1000

输出格式

1个整数,表示具有该性质数的个数

输入输出样例

输入

6

输出

6

解释:满足的数分别为6,16,26,126,36,136


1.首次尝试

解法一 递归

显然,最简单直接的做法就是递归(将一个大问题规约成更小规模的子问题,通过子问题的求解完成原问题)
假设:
当输入为4时,满足的其性质的数有——4本身,加上其规模1/2^1/_21/2​的子问题解的个数,即 4, 24, 124
根据我们的分析,算法的也就不难写出来了,递归代码如下

int fun(int n) {	//版本一 递归
	int cnt = 0;
	for(int i=1; i<=n/2; i++) {
		cnt += fun(i);
	}
	return cnt+1;
}

2.超时

递归的方法虽然简单直观,但是提交没过——超时了,递归深度太深导致爆栈了,这时候我们就需要思考能不能改进?能不能进一步优化?

启发

还记得最初在学习递归的时候,一个经典的案例就是斐波那契数列f(n)=f(n1)+f(n2),n2f(n) = f(n-1) + f(n-2), n\geq2f(n)=f(n−1)+f(n−2),n≥2
我们发现,当递归调用的时候,其中某一个子树的值可能会被重复计算很多次,很费时间:
图片摘自网络,侵删
例如图中f(2)f(2)f(2)的值就被重复计算了三次,那么我们能不能让它只计算一次呢?

动态规划(DP)思想

对于斐波那契数列的一种优化方法就是利用数组暂存中间值,这样就避免了重复计算
那么什么又是动态规划?

遵循动态规划算法是将问题递归地分解成更简单的重叠子问题,通过子问题的解决来解决原问题
动态规划目前已广泛应用于经济学、计算机视觉、语音识别、人工智能、计算机图形学和生物信息学

在斐波那契数列的例子中,多次计算的f2f(2)f(2)的值,这样就是一个重叠的子问题,我们应避免重复计算带来的时间开销,于是引入了数组——保存每一次计算的值(这样一个过程称为记忆, 对于提高递归算法效率是很重要的技术)

3.优化

我们有了思路,下面的关注点就在于寻找该问题的一个递推关系式
我们先列出来一部分:(其中f(n)f(n)f(n)表示自然数nnn所具有上述三种性质的个数)

f(1)=1f(1) = 1f(1)=1
f(2)=2f(2) = 2f(2)=2 f(3)=2f(3) = 2f(3)=2
f(4)=4f(4) = 4f(4)=4 f(5)=4f(5) = 4f(5)=4
f(6)=6f(6) = 6f(6)=6 f(7)=6f(7) = 6f(7)=6
f(8)=10f(8) = 10f(8)=10 f(9)=10f(9) = 10f(9)=10

我们可以看出:

综上,我们可以得出递推式:

  1. f(1)=1f(1) = 1f(1)=1
  2. n>1n>1n>1时
    • nnn为奇数,f(n)=f(n1)f(n) = f(n-1)f(n)=f(n−1)
    • nnn为偶数,f(n)=f(n1)+f(n/2)f(n) = f(n-1) + f(n/2)f(n)=f(n−1)+f(n/2)

代码

long fun(int n){
	long a[n+1];

	a[0] = 1;
	
	for(int i=1; i<n+1; i++){
		if(i%2 != 0)    //奇数
			a[i] = a[i-1];
		else
			a[i] = a[i-1] + a[i/2];
	}
	
	return a[n];
}

4.小结

学习算法的道路漫长且艰巨,唯有不断打磨自己不断思考,挖掘其背后的本质,才能取得进步

标签:nnn,洛谷,递归,int,自然数,个数,2f,计算,P1028
来源: https://blog.csdn.net/Apol1o_/article/details/104878295