题干
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;
}