C. Factorials and Powers of Two(最详细二进制枚举、爆搜)

一、题目

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


五、总结

        做题要多想想

  • 3
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 5
    评论
评论 5
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值