开启掘金成长之旅!这是我参与「掘金日新计划 · 12 月更文挑战」的第10天,点击查看活动详情
前言
本文是关于Leetcode刷题——二进制1个数数列的C语言实现。
新手一个,水平比较低,还请包涵。
题目描述
小q最近迷上了各种好玩的数列,这天,他发现了一个有趣的数列,其递推公式如下:
f[0]=0 f[1]=1; f[i]=f[i/2]+f[i%2];(i>=2)
现在,他想考考你,问:给你一个n,代表数列的第n项,你能不能马上说出f[n]的值是多少,以及f[n]所代表的值第一次出现在数列的哪一项中?(这里的意思是:可以发现这个数列里某几项的值是可能相等的,则存在这样一个关系f[n'] = f[n] = f[x/2]+f[x%2] = f[x]...(n'<n<x) 他们的值都相等,这里需要你输出最小的那个n'的值)(n<10^18)
输入描述: 输入第一行一个t 随后t行,每行一个数n,代表你需要求数列的第n项,和相应的n' (t<4*10^5)
输出描述: 输出每行两个正整数 f[n]和n',以空格分隔
输入: 2 0 1
输出: 0 0 1 1
思路
实际上这个数列写成的函数的功能恰好是求取一个数二进制位上1的个数,这里用递归稍微推演一下就会发现i一直变直到小于2。
n&1和n%2有同样效果都可以用来判断n是不是奇数,如果是奇数,n&1==1,n%2==1;是偶数,n&1==0,n%2==0。
n>>1与n/2效果一样,n的二进制位右移后的值就等于除以2。
题目中f[i/2]+f[i%2]-->i是偶数时:f[i/2]; i是奇数时:f[i/2]+1(是奇数时+1)。
意思就是说:由几个奇数构成,最后答案就等于几。判断奇数又由n&1判断,除以二又由n>>1替代,所以有几个奇数构成->01串中有几个1->最后答案
f[0]=0 f[1]=1; f[i]=f[i/2]+f[i%2];(i>=2) 其实这个式子就是不断把二进制的i右移一位,再看一下i是不是奇数(二进制最后一位是不是1),如果是就+1,那么任何数都能化成f[1]+X,X= i的二进制中1的个数 - 1。
最早出现的n'就是2的0次方一直加到2的X次方(2的a次方-1),就是n'的二进制全是1,并且1的个数=i的二进制中1的个数。
其实吧这个题让我难受的是另一点:回溯找到最小n',一开始想的是小于n的数一个一个放到f()递归得到值后再比较,直到遇到相同的,但是复杂度太大不合要求。
更好的方法:fn就是二进制位1的个数,然而限定1的数量后,数的大小就由1的排布决定,全部1向低位靠拢不间隔出0就得到最小的数值。n和n'的fn相同,也就是二进制位上1的个数相同,只是1的排布不同致使值的差异。
代码
#include<stdio.h>
long long fun(long long n)
{
if(n <= 1)
return n;
if(n & 1)
return fun(n >> 1) + 1;
return fun(n >> 1);
}
int main()
{
long long t = 0;
int i = 0;
int j = 0;
scanf("%lld", &t);
long long n = 0;
int fn = 0;
long long n_ = 0;
for(i = 0; i < t; i++)
{
scanf("%lld", &n);
fn = fun(n);
printf("%lld ", fn);
for(j = 0; j < fn; j++)
{
n_ = n_ * 2 + 1;
}
printf("%lld\n", n_);
n_ = 0;
}
return 0;
}