最小的k值

目录

1.题目

2.解题思路

3.代码思路

4.代码实现


1.题目

题目描述

现在给出一个正整数 n ,找到最小值 k 使得 n 可以表示为k个不同的强大的数字的和,或者说没有这样的数字 k.
强大的数字:2的幂或者是任意数阶乘。即2d 或 x! (x表示任意的非负数)
例:1,4,6 都是强大的数字,因为1=1!,4=22 和 6 =3! ,而7,10,18不是。

输入

每个测试包括多个测试样例,每一行包括测试用例的数量 T (1≤T≤100)
一个测试用例只包括一行,包括一个整数 n (1≤n≤1012)

输出

对于每个测试用例,将最小值 k 输出。
如果不存在k个强大的数字的和为n,那么输出-1.

样例输入

4
7
11
240
17179869184

样例输出

2
3
4
1

2.解题思路

题意:给定你一个n,问你最少用几个不同的强大的数加起来的和等于n,输出最少使用的个数k。

解题思路:

既然是用数个强大的数加起来,而强大的数分为两类,一类是阶乘数,一类是2的幂,那我们需要从这两类数里面选出合适的数,那是先从阶乘数那里选,还是先从2的幂那里选,还是说两类里随机选取?

我们的思路是先选阶乘数。

因为每个阶乘数对应的一个十进制数的大小是一定的,且是不规律的。而2的幂,因为任何十进制数都可以转化为一个二进制数,即转化为一组2的幂,所以反之可用一组2的幂去组成任意一个十进制数。那就是说,先选了任意个阶乘数,我都能用一组2的幂去构成(n-任意个阶乘数的和),且保证了每一个强大的数都不同。

如果先从2的幂那里选,然后再从阶乘数那里选出一组数来构成(n-2的幂的和),因为每个阶乘数对应的十进制数不规律,所组成的和不一定能刚好构成(n-2的幂的和),这就不合题意了,若再从2的幂那里挑则不如先选阶乘数方便,故不用这种选法。

如果在这两类里随机选择,则可能还会出现第二种选法的问题,且不方便,故也不用这种选法。

确定了先选阶乘数,那下一步我们如何确保,每一种选择阶乘数的情况我们都考虑到呢?

我们采用二进制枚举的方式来枚举所有可能的选择阶乘数的情况。(1代表选择对应的阶乘,0代表不选,一串01序列即可表示每种对应阶乘选没选的情况,n个阶乘就用n个二进制来枚举,枚举从都不选到都选,即全为0到全为1)

在每一次确定选哪些阶乘数后,再将剩下的数转换为二进制,并将其使用的2的幂分离出来,记录每一次选择。

在将所有可能的选择阶乘数的情况枚举的时候,每一次比较更新选用强大的数的个数。

最后得到k值并输出。

3.代码思路

cnt:用于存储每次枚举时所用到的强大的数字的个数

ans:用于比较每次枚举时得到的cnt,从而获得最终最小的k值

sum:用于存储该次枚举的阶乘数的总和

jcs:一个用于存储每个阶乘对应阶乘数的数组

t:用n减去阶乘数总和后剩下的值

        先将需要用到的阶乘数进行一个预处理,存入一个数组中。在完成输入后,将阶乘数(本次最大为14)可能的情况,从0到2^{14}-1枚举枚举1遍,将每次选择的阶乘数都分辨出来,并加到sum上,同时,cnt++,如果sum大于n就break(够大就没必要接着枚举了),让t=n-sum,再将t对应的各个2的幂分离出来,同时cnt++,然后同ans比较,若cnt比ans小,则更新ans.将所有情况枚举完后,此时的ans为最小的k值,将其输出即可。

4.代码实现

#include<iostream>

using namespace std;

const int N=1e5;
long long sum,n,t;
int cnt,ans;
long long jcs[20];

void jc()
{
    jcs[1]=1;
    for(int i=2;i<=14;i++)  jcs[i]=jcs[i-1]*i;
}

int main()
{

    int T;
    cin >>T;
    jc();///阶乘数预处理
    while(T--)
    {
        cin >>n;
        ans=N;
        for(int i=0;i<(1<<14);i++)
        {
            sum=0;
            cnt=0;
            for(int j=0;j<14;j++)
            {
                if((i>>j)&1)    cnt++,sum+=jcs[j+1];
            }
            if(sum>n)   break;
            t=n-sum;
            for(int j=0;j<64;j++)
            {
                if((t>>j)&1)    cnt++;
            }
            ans=min(ans,cnt);
        }
        cout <<ans <<endl;
    }
    return 0;
}

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值