数的计数(加强版)
描述
先输入一个自然数n(n≤3000000),然后对此自然数按照如下方法进行处理
1·不作任何处理:
2·在它的左边加上一个自然数,但该自然数不能超过原数的一半;
3·加上数后,继续按此规则进行处理,直到不能再而 自然数为止。
例如n=6
6
16
26
126
36
136
所以满足要求的个数为6。
格式
输入格式
包含多个测试数据,每行是一个整数n(1<=n<=3000000)
输出格式
一个整数,表示解的个数(保证不超过50位)
样例1
样例输入1
样例输出1
限制
各个测试点1s
</div>
</div></div>
这个题对于我这种菜鸡就一个字 难 ,经过多方面的查找及思考, 算是摸清了点皮毛。
关键点
- 包含多个测试数据,可以用
while(scanf("%d", &a) != EOF)
; - n 的范围非常的大,n 越大也就说明满足要求的数就会越来越大, 反正是大到不能赋值给数组,目前个人了解的是高精度压位的解法。
- 题中还需要递推。
个人理解
- 递推,直接使用结论吧
在纸上多算一些数就会明白其中的规律, 其中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] 4 F[5] =F[4]=4 F[6] =F[5]+F[3]=4+2=6 F[7] =F[6]=6 F[8] =F[7]+F[4]=6+2=10 F[9] =F[8]=10 F[10] =F[9]+F[5]=10+4=14 F[11] =F[10]=14 F[12] =F[11]+F[6]=14+6=20 F[13] =F[12]=20 F[14] =F[13]+F[7]=20+6=26 F[15] =F[14]=26 F[16] =F[15]+F[8]=26+10=36 F[17] =F[16]=36 F[18] =F[17]+F[9]=36+10=46 F[19] =F[18]=46 F[20] =F[19]+F[10]=46+14=60 F[21] =F[20]=60 F[22] =F[21]+F[11]=60+14=74 F[23] =F[22]=74 F[24] =F[23]+F[12]=74+20=94 F[25] =F[24]=94 F[26] =F[25]+F[13]=94+20=114 F[27] =F[26]=114 F[28] =F[27]+F[14]=114+26=140 F[29] =F[28]=140 F[30] =F[29]+F[15]=140+26=166 F[31] =F[30]=166 F[32] =F[31]+F[16]=166+36=202 F[33] =F[32]=202 F[34] =F[33]+F[17]=202+36=238 F[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] 0 0 0 0 4 F[6] 0 0 0 0 6 F[7] 0 0 0 0 6 F[8] 0 0 0 1 0 F[9] 0 0 0 1 0 F[10] 0 0 0 1 4 F[11] 0 0 0 1 4 F[12] 0 0 0 2 0 进位 F[13] 0 0 0 2 0 F[14] 0 0 0 2 6 F[15] 0 0 0 2 6 F[16] 0 0 0 3 6 进位 F[17] 0 0 0 3 6 … … … … … … F[24] 0 0 0 9 4 进位 F[25] 0 0 0 9 4 … … … … … … F[30] 0 0 1 6 进位 6 进位 F[31] 0 0 1 6 6 F[32] 0 0 2 0 进位 2 进位 F[33] 0 0 2 0 2 … … … … … …
可以在纸上把压位压大一点,再去模拟一遍
#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;
}