一、题目
C. Factorials and Powers of Twohttps://codeforces.com/contest/1646/problem/C
二、题目大意及其分析
1、题目意思:一个数能否由一些 2的次方 或者 n的阶乘 等数组成(并且这些数要不一样的),求最小的数量。(例如:7=6(来自于3!)+1(来自于0!或者1!或者 2的0次方) )
2、一个数字肯定是可以由二进制表示出来的,所以我们可以先枚举所有阶乘可以组成的数,再用剩下的数用二进制表示(如27=4!+3),所以可以有(27=4!+2+1),答案就是3;
3、枚举阶乘可以组成数,可以爆搜,也可以用二进制枚举;具体可以看代码,不懂得可以下面评论。
三、爆搜枚举阶乘可以组成数 的代码
#include<iostream>
#include<vector>
#include<algorithm>
using namespace std;
typedef long long ll;
ll t = 1,x,ans,f[20];
ll count(ll x){ll res = 0; while (x) res += x & 1,x >>= 1; return res;}
//爆搜去寻找那个男人
void dfs(ll pos,ll sum,ll ant){
if (sum > x) return;
if (pos == 15){
ans = min(ans,ant + count(x - sum));
return;
}
//不需要这个f[i]值的,继续往下搜索
dfs(pos + 1,sum,ant);
//需要f[i]的,然后继续往下搜索
dfs(pos + 1,sum + f[pos],ant + 1);
}
int main()
{
cin >> t;
f[0] = 1;
for (ll i = 1;i <= 14;i += 1) f[i] = f[i - 1] * i;
while (t --){
cin>>x; ans = 1e18;
dfs(0,0,0);
cout<<ans<<endl;
}
return 0;
}
四、二进制枚举阶乘可以组成数 的代码
#include<iostream>
#include<vector>
#include<algorithm>
#define LL long long
using namespace std;
vector<LL> factBook;
//预处理出1到14的阶乘,可知15的阶乘一定是大于n的
void preprocess()
{
LL curFact = 1;
for(int i = 0; i < 15; i++)
{
curFact *= max(1,i);
if(i > 2 || i == 0) factBook.push_back(curFact);
}
}
//计算出一个数中用二进制表示的位数
int getOneCount(LL n)
{
int cnt = 0;
while(n > 0)
{
cnt++;
n -= (n & -n);
}
return cnt;
}
//开始对阶乘可能组成的数的枚举
int getPowerCount(LL n)
{
const int M = factBook.size();
//求最大的可能枚举的数量
const int maxMask = (1 << M);
int best = 99;
//然后进行每个数的枚举行动
for(int mask = 0; mask < maxMask; mask++)
{
int penalty = 0;
LL remain = n;
for(int i = 0; i < M; i++)
{
if((mask>>i) &1 )
{
remain -= factBook[i];
penalty++;
}
}
if(remain < 0) continue;
penalty += getOneCount(remain);
best = min(best, penalty);
}
if(best >= 99) return -1;
return best;
}
int main()
{
preprocess();
int tc; scanf("%d",&tc);
for(int test_id = 1; test_id <= tc; test_id++)
{
LL n; scanf("%lld",&n);
const int ans = getPowerCount(n);
printf("%d\n", ans);
}
return 0;
}
五、总结
做题要多想想