C. Factorials and Powers of Two(dfs)

题干

A number is called powerful if it is a power of two or a factorial. In other words, the number m is powerful if there exists a non-negative integer d such that m=2d or m=d!, where d!=1⋅2⋅…⋅d (in particular, 0!=1). For example 1, 4, and 6 are powerful numbers, because 1=1!, 4=22, and 6=3! but 7, 10, or 18 are not.

You are given a positive integer n. Find the minimum number k such that n can be represented as the sum of k distinct powerful numbers, or say that there is no such k.

Input
Each test contains multiple test cases. The first line contains the number of test cases t (1≤t≤100). Description of the test cases follows.

A test case consists of only one line, containing one integer n (1≤n≤1012).

Output
For each test case print the answer on a separate line.

If n can not be represented as the sum of distinct powerful numbers, print −1.

Otherwise, print a single positive integer — the minimum possible value of k.

Example
inputCopy
4
7
11
240
17179869184
outputCopy
2
3
4
1
Note
In the first test case, 7 can be represented as 7=1+6, where 1 and 6 are powerful numbers. Because 7 is not a powerful number, we know that the minimum possible value of k in this case is k=2.

In the second test case, a possible way to represent 11 as the sum of three powerful numbers is 11=1+4+6. We can show that there is no way to represent 11 as the sum of two or less powerful numbers.

In the third test case, 240 can be represented as 240=24+32+64+120. Observe that 240=120+120 is not a valid representation, because the powerful numbers have to be distinct.

In the fourth test case, 17179869184=234, so 17179869184 is a powerful number and the minimum k in this case is k=1.

题意

现有一个元素不重复的集合,元素值不大于 1e12,且均为 2 的次方 或 某数的阶乘;要求在上述集合中取出若干个元素,使它们的和为 n ,问最少需要几个元素。

思路

先打表,计算出集合中每个元素的值,每个元素都有 取 和 不取 两种选择,遍历所有情况,找到和为 n 的最少元素数量。

剪枝:记录集合中剩余元素的和,若 集合中剩余元素之和 + 已选元素之和 < n,则停止遍历。

代码

#include <iostream>
#include <cstdio>
#include <algorithm>
#include <cstdio>
#include <set>

using namespace std;
typedef unsigned long long ll;

const ll N = 1e12+10;

ll f[60], re = 0;
int ans,s = 0;//s==53

void make_fund(){//打表
    ll a = 1,b = 1;
    set<ll> se;
    while(a<=N){
        se.insert(a);
        a*=2;
    }
    a=1;
    while(a<=N){
        a*=b;
        se.insert(a);
        b++;
    }
    set<ll>::iterator it;
    for(it = se.begin();it!=se.end();it++){
        f[s++] = *it;
        re+=*it;
    }
//    printf("%d\n",s);
//    for(int i=0;i<s;i++){
//        printf("%lld\n",f[i]);
//    }
}
void dfs(ll va,int ii,int g,ll st){
    if(va==0){
        if(g<ans) ans = g;
        return;
    }
    //st<va 为 剩余可选择的数字之和 小于要组成的数字
    if(st<va||ii<0)
        return;

    dfs(va,ii-1,g, st-f[ii]);
    if(f[ii]<=va)
        dfs(va-f[ii], ii-1, g+1, st-f[ii]);
}
int main()
{
    make_fund();
    int t;
    scanf("%d",&t);
    while(t--){
        ll n;
        ans = s;
        scanf("%lld",&n);
        dfs(n, s-1, 0, re);
        printf("%d\n",ans);
    }
    return 0;
}
  • 1
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值