CF #774 ——C(二进制枚举)

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;
}
  • 4
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

画江湖がº

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值