洛谷P1028 数的计算

题目描述
我们要求找出具有下列性质数的个数(包含输入的自然数nn):
先输入一个自然数n(1<=n≤1000),然后对此自然数按照如下方法进行处理:
不作任何处理;
在它的左边加上一个自然数,但该自然数不能超过原数的一半;
加上数后,继续按此规则进行处理,直到不能再加自然数为止.
输入格式
1个自然数
输出格式
11个整数,表示具有该性质数的个数。
输入输出样例
输入 #1
6
输出 #1
6
说明/提示
满足条件的数为
6,16,26,126,36,136
传送门洛谷P1028

第一篇题解,先说一下思路

这个使用递归公式计算的

f(n) = ∑ i = 0 n 2 f ( i ) \sum_{i=0}^\frac{n}{2} f(i) i=02nf(i)

因此这个题实际是一个求前n项和的过程

这里我使用的是与递归相反的一个正向从前往后的思路,也就是从递归的根节点逐步向上,避免重复(其实前n项和就是不断重复)

接下来我们对公式进行优化

2 n / 2 = ( 2 n + 1 ) / 2 ⇒ { f ( 2 n ) = ∑ i = 0 n f ( i ) F ( 2 n + 1 ) = ∑ i = 0 n F ( i ) 2n/2 = (2n+1)/2 \Rightarrow \begin{cases} f(2n) =\sum_{i=0}^{n} f(i) & \\ \\\\ F(2n+1) = \sum_{i=0}^{n} F(i) & \end{cases} 2n/2=(2n+1)/2f(2n)=i=0nf(i)F(2n+1)=i=0nF(i)

因此我们发现f(2n)==f(2n+1),并且前n项和连续,因此我们节省了一半的空间与时间

对于数组num, num[i/2] = f(n/2)(这里n是自然数n不要混淆)

由于之前奇数偶数的性质我们开始对问题进行分类

f ( n ) = { f ( 2 n ) =   f ( n ) + f ( 2 n − 1 ) F ( 2 n + 1 ) =   F ( 2 n ) f(n) = \begin{cases} f(2n) =\ f(n)+f(2n-1) & \\ \\\\ F(2n+1) = \ F(2n) & \end{cases} f(n)=f(2n)= f(n)+f(2n1)F(2n+1)= F(2n)

接下来证明n为偶数的公式

f ( 2 n ) f(2n) f(2n) - f ( 2 n − 1 ) f(2n-1) f(2n1)

= ∑ i = 0 n f ( i ) \sum_{i=0}^{n} f(i) i=0nf(i) - ∑ i = 0 n − 1 f ( i ) \sum_{i=0}^{n-1}f(i) i=0n1f(i)
= f ( n ) f(n) f(n)

f ( 2 n ) f(2n) f(2n) = f ( n ) f(n) f(n) - f ( 2 n − 1 ) f(2n-1) f(2n1)

到了这里完整的思路就成型了

对于自然数n,我们只需要求出前n/2项和就可以完成计算

而因为 f ( 2 n ) f(2n) f(2n) = f ( 2 n + 1 ) f(2n+1) f(2n+1)的特性我们对空间进行压缩,同时也节约了一半的时间,近似达到了O(n/4)(速度在80ms近似相同)

值得注意的是因为空间的压缩前n项和公式发生了变化

F ( n ) F(n) F(n)

= ∑ i = 0 n 2 f ( i ) \sum_{i=0}^\frac{n}{2} f(i) i=02nf(i)

= ∑ i = 0 k f ( i ) \sum_{i=0}^{k} f(i) i=0kf(i)

= ∑ i = 0 k / 2 f ( i ) \sum_{i=0}^{k/2} f(i) i=0k/2f(i)

f ( n ) = { 1 , n = = 1 2 ∗ F ( n ) , k & 1 = = 1 2 ∗ F ( n ) −   f ( k / 2 ) , k & 1 = = 0 f(n) = \begin{cases} 1&,n == 1\\ 2*F(n) & ,k\&1 == 1 \\ 2*F(n) - \ f(k/2) & ,k\&1 == 0\end{cases} f(n)=12F(n)2F(n) f(k/2),n==1,k&1==1,k&1==0
(n为自然数,k = n/2)

解释一下第一个情况大于1时组合都包括本身和1所以累加的时候2* f ( 0 ) f(0) f(0)就代表这两个值,而1属于特例所以特殊处理

而第三种之前说过 f ( 2 n ) f(2n) f(2n)= f ( 2 n + 1 ) f(2n+1) f(2n+1)

所以2* f ( n ) f(n) f(n)时相对于k是偶数的,重复加了一次需要减去

f(8)

= ∑ i = 0 4 f ( i ) \sum_{i=0}^{4} f(i) i=04f(i)

= ∑ i = 0 2 F ( i ) \sum_{i=0}^{2} F(i) i=02F(i)- f ( 2 ) f(2) f(2)

sum[8]

= sum[4]+sum[3]+sum[2]+sum[1]+1

= 2*(sum[2]+sum[1]+sum[0])-sum[2]

第一次写题解废话多,逻辑可能不严谨,希望大佬们能指正


#include <iostream>
#include <cstring>
#include <map>
int num[1000];

inline int read(){
    char c = getchar();
    int num = 0;
    while(c<'0'||c>'9'){
        c = getchar();
    }
    while(c>='0'&&c<='9'){\
        num = (num<<1)+(num<<3)+(c-'0');
        c = getchar();
    }
    return (num)>>1;
    //f(2n)==f(2n+1);

}
int main()
{
    num[0] = 1;//此处n代表原自然数的一半
    int sum =1,n = read(),len = n>>1;
    bool f = n&1;
    int i;
    //这里的作用是f(i)的值应为n/2的前n项和,而这里因为f(2n) = f(2n+1),因此将空间压缩,同时也降低了一半的时间消耗
    for(i=1;i<=len;i++){
        num[i] = num[i-1]+num[i>>1];
        sum+=num[i];
    }
    //对于的值都包含本身和一的前缀,而对于1只包含本身因此要减一
    if(n){
    if(f&1){
        std::cout<<(sum<<1);
    }else{
        std::cout<<(sum<<1)-num[i-1];
    }}
    else
        std::cout<<1;
    return 0;
}

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值