C. Factorials and Powers of Two
题意:
如果一个数为 2的幂次 或者是 阶乘数,那么定义这个数为漂亮数。
现在给定一个数 n,问最少可以由多少个不同的漂亮数构成?
1 ≤ n ≤ 10^12
分析:
已知任何一个数都可以用若干个 2的幂次数 相加而成,2的那些幂次数 可以用将这个数化为二进制后,1的位置和个数确定。
观察到 n 的范围不大,在这个范围内的阶乘数一共14个,所以可以遍历这些数到底用了哪几个。(二进制枚举)
然后减去这些阶乘数之后,判断需要用多少2的幂次数构成。
答案 与 阶乘数+2的幂次数个数 取min。
官方教程
如果问题要求代表作为仅两个的不同幂的总和(没有阶乘),那么有一种独特的方法可以做到这一点,使用二进制表示并且术语的数量将是等于的位数在这个二进制表示中。让我们用这个数字表示ones(n).如果我们修复了我们要在总和中使用的阶乘,那么由于上面的观察,其余的项都是唯一确定的。
所以,要解决这个问题,迭代包括或不包括每个阶乘的所有可能性就足够了(最多14!)并为它们中的每一个计算总和中使用的术语数。如果我们使用f阶乘,它们的总和是s,则项数可以计算为f+ones(n-s). 所有这些数字中的最小值将是答案。
预期的复杂性:O(2^k) k是最大的正整数,使得k!<=n
二进制枚举:
一共 n 个数,判断拿了哪几个。
for(int i=0; i<(1<<n); i++){ //一共2^n次情况,状态压缩为一个数。
for(int j=0;j<n;j++){ //通过枚举这个数的二进制每一位来确定这个物品是否拿了
if(i & (1<<j)) ... //如果第j个位置为1,那么就拿了第j个位置。
}
}
AC代码:
#include<bits/stdc++.h>
using namespace std;
#define int long long
const int N = 200010, mod = 1e9+7;
int T, n, m, k;
int a[N];
int check(int x)//阶层
{
int ans=1;
for(int i=1;i<=x;i++) ans*=i;
return ans;
}
signed main()
{
int cnt=0;
n = 1000000000000;
for(int i=1;i<=n;i++)
{
int x = check(i);
if(x > n) break;
a[++cnt] = x;
//cout<<a[cnt]<<"\n";
}
cin>>T;
while(T--)
{
cin>>n;
int ans = 1e9;
for(int i=0;i<(1ll<<14);i++)//2^14
{
int cnt=0, sum=0;
for(int j=0;j<14;j++)
{
if(i & (1<<j)) cnt++, sum+=a[j+1];//,cout<<a[j+1]<<"\n"
}
//cout<<sum<<"\n";
if(sum > n) break;
int t = n-sum;//n减去你选的数之后的最小值是那数中二进制1的个数
//cout<<t<<"\n";
for(int j=0;j<64;j++)//2^j
{
if(t & (1ll<<j)) cnt++;
}
ans = min(ans, cnt);
}
cout<<ans<<"\n";
}
return 0;
}