C. Factorials and Powers of Two_暴搜or二进制枚举

C. Factorials and Powers of Two暴搜or二进制枚举

题目大意:

给一个数n,问其最少能由几个不同“强数”相加得到。所谓“强数”,即某数的阶乘或者2的次幂。

思路和代码:

首先是一定有答案的,因为任何数字都能表现为2的次幂相加。

其次是n的范围是1e12,所以14!或2的48次方即可。

我一开始的思路是阶乘和次幂哪个大减哪个,很明显是错的。

再然后用搜索去做了一下,因为数据比较小,暴搜一下是否拿i!即可。但是zls说一般情况别用内置函数,所以再写个返回二进制中1的个数的函数即可。

ll fac[20] ;

void init(){
	fac[1] = fac[0] = 1 ;
	rep(i , 2 , 17) fac[i] = i * fac[i - 1] ;
}

ll ans ;

void dfs(int now , ll num , ll take){
	
	if(now >= 17) return ;
	ans = min(ans , 1LL * (__builtin_popcountll(num - fac[now])) + take + 1) ;
	dfs(now + 1 , num - fac[now] , take + 1) ;
	ans = min(ans , 1LL * (__builtin_popcountll(num)) + take) ;
	dfs(now + 1 , num , take) ;
}
/*
__builtin_popcountll(x)是内置函数,直接返回x的二进制位中有几个1
*/
void solve(){
	ll n ;
	cin >> n ;
	ans = INF ;
	dfs(1 , n , 0) ;
	cout << ans << "\n" ;
}

然后看到学弟的代码,就很牛啊,用二进制压缩了所有的拿取阶乘的方案。

/*
%一下
*/
ll fac[15];
ll lowbit(ll x)
{
	return x & (-x);
}
int cnt(ll x)
{
	int res = 0;
	while (x)
	{
		res++;
		x -= lowbit(x);
	}
	return res;
}
int main()
{
	ios::sync_with_stdio(false);
	cin.tie(0), cout.tie(0);
	int t;
	cin >> t;
	fac[0] = 1;
	for (int i = 1;i < 15;i++)
	{
		fac[i] = fac[i - 1] * i;
	}
	while (t--)
	{
		ll n;
		cin >> n;
		int ans = 0x3f3f3f3f;
		for (int i = 0;i < 1 << 15;i++)
		{//枚举所有可能的阶乘选择情况
			int st = i;
			ll num = n;
			for (int j = 0;j < 15;j++)
			{
				if (st >> j & 1)
				{
					num -= fac[j];
				}
			}
			if (num >= 0)
			{
				ans = min(ans, cnt(num) + cnt(i));
			}
		}
		if(ans==0x3f3f3f3f) cout << -1 << endl;
		else cout << ans << endl;
	}
}

小结:

时间复杂度要算清楚,这题我往暴搜的地方想了一下,但是没仔细算复杂度就没做下去..

posted @ 2022-04-25 10:36  tyrii  阅读(110)  评论(0)    收藏  举报