数的计数(加强版)

数的计数(加强版)

描述

先输入一个自然数n(n≤3000000),然后对此自然数按照如下方法进行处理

1·不作任何处理:

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

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

例如n=6
6
16
26
126
36
136

所以满足要求的个数为6。

格式

输入格式

包含多个测试数据,每行是一个整数n(1<=n<=3000000)

输出格式

一个整数,表示解的个数(保证不超过50位)

样例1

样例输入1

6

样例输出1

6

限制

各个测试点1s

    </div>
  </div></div>

这个题对于我这种菜鸡就一个字 难 ,经过多方面的查找及思考, 算是摸清了点皮毛。

关键点

  • 包含多个测试数据,可以用 while(scanf("%d", &a) != EOF);
  • n 的范围非常的大,n 越大也就说明满足要求的数就会越来越大, 反正是大到不能赋值给数组,目前个人了解的是高精度压位的解法。
  • 题中还需要递推。

个人理解

  1. 递推,直接使用结论吧

在纸上多算一些数就会明白其中的规律, 其中F[1]=1,F[2]=2是看得出来的

n 是奇数:F[n] = F[n - 1]

n 是偶数:F[n] = F[n - 1] + F[n / 2] = F[n - 2] + F[n / 2]

比如

数字数字
F[4]4F[5]=F[4]=4
F[6]=F[5]+F[3]=4+2=6F[7]=F[6]=6
F[8]=F[7]+F[4]=6+2=10F[9]=F[8]=10
F[10]=F[9]+F[5]=10+4=14F[11]=F[10]=14
F[12]=F[11]+F[6]=14+6=20F[13]=F[12]=20
F[14]=F[13]+F[7]=20+6=26F[15]=F[14]=26
F[16]=F[15]+F[8]=26+10=36F[17]=F[16]=36
F[18]=F[17]+F[9]=36+10=46F[19]=F[18]=46
F[20]=F[19]+F[10]=46+14=60F[21]=F[20]=60
F[22]=F[21]+F[11]=60+14=74F[23]=F[22]=74
F[24]=F[23]+F[12]=74+20=94F[25]=F[24]=94
F[26]=F[25]+F[13]=94+20=114F[27]=F[26]=114
F[28]=F[27]+F[14]=114+26=140F[29]=F[28]=140
F[30]=F[29]+F[15]=140+26=166F[31]=F[30]=166
F[32]=F[31]+F[16]=166+36=202F[33]=F[32]=202
F[34]=F[33]+F[17]=202+36=238F[35]=F[34]=238

这规律应该很明显了吧!

2 高精度压位,压多少为就看心情了,对于题目的话可以压到个 10^9 或者再大一点,前提就是数组里能存的下。

  • 先看我压的位

      int MOD=1E+9;   //高精度压位满10^9进位
      int ans[3000001][6];   //ans中的二维标志进位
    
  • 解释:
    由于用一维数组无法存下非常大的数,但是可以用二维数组来存放, 如果要存放数 87456120007258369700056123456123789 ,想想就这么大的一个数用F[2] 来存, 肯定是扯但, 但是用 F[2][6] 来存,可以考虑一下, 根据上面给出的代码来存

      首先 F[2][6] 初始化为 0;
      将 87456120007258369700056123456123789 % MOD 直到为0就停止;
      将此数取模分开 87456120 007258369 700056123 456123789 , 接下来就存入二维数组了;
      F[2][0]=456123789 , F[2][1]=700056123, F[2][2]=007258369 , F[2][3]=87456120 ,F[2][4]=F[2][5]=F[2][6]=0
    
  • 实践环节

根据最上面给的表来压位

代码

int MOD=10;   //高精度压位满10进位
int F[3000001][4] = {0};   //F中的二维标志进位
for (int i = 3; i <= 3000000; i++)
{
    if (i % 2 == 1)
    {
        for (int j = 0; j < 4; j++)
        {
            F[i][j] = F[i-1][j];
        }
    }
    else
    {
        for (int j = 0; j < 4; j++)
        {
            F[i][j] += F[i-2][j] + F[i/2][j];//F[i][j] += F[i-1][j] + F[i/2][j]好像也可以
                if (F[i][j] >= MOD)
                {
                    F[i][j] %= MOD;
                    F[i][j+1]++;
                }
        }
    }
}
F[n]F[n][4]F[n][3]F[n][2]F[n][1]F[n][0]
F[5]00004
F[6]00006
F[7]00006
F[8]00010
F[9]00010
F[10]00014
F[11]00014
F[12]00020 进位
F[13]00020
F[14]00026
F[15]00026
F[16]00036 进位
F[17]00036
F[24]00094 进位
F[25]00094
F[30]0016 进位6 进位
F[31]00166
F[32]0020 进位2 进位
F[33]00202

可以在纸上把压位压大一点,再去模拟一遍

#include <cstdio>
#include <cstring>
#include <cstdlib>

int MOD=1E+9;   //高精度压位满10^9进位
int ans[3000001][6];   //ans中的二维标志进位

void Enhance_Count()
{
    for (int i = 3; i <= 3000000; i++)
    {
        if (i % 2 == 1)
        {
            for (int j = 0; j < 6; j++)
            {
                ans[i][j] = ans[i-1][j];
            }
        }
        else
        {
            for (int j = 0; j < 6; j++)
            {
                ans[i][j] += ans[i-2][j] + ans[i/2][j];
                    if (ans[i][j] >= MOD)
                    {
                        ans[i][j] %= MOD;
                        ans[i][j+1]++;
                    }
            }
        }
    }
}

int main()
{
    memset(ans, 0, sizeof(ans));
    ans[1][0] = 1;
    ans[2][0] = 2;
    Enhance_Count();

    int n;
   // scanf("%d", &n);

    while (scanf("%d", &n) != EOF)
   {
        int flag = 0;
        for (int i = 5; i >= 0; i--)
        {
            if (flag == 0)
            {
                if (ans[n][i])
                {
                    printf("%d", ans[n][i]);
                    flag = 1;  //输出最高几位的数后,标记flag =1, 方便输出低位的数(因为低位数字前可能补0)
                }
            }
            else
            {
                printf("%09d", ans[n][i]);
            }
        }
        printf("\n");
    }
    return 0;
}
  • 2
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值