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;
}
}
小结:
时间复杂度要算清楚,这题我往暴搜的地方想了一下,但是没仔细算复杂度就没做下去..

浙公网安备 33010602011771号