CF1303D Fill The Bag
题意:给你两个数字 n,m
然后下面跟着 m个 2的任意次方 的数 让你用这m个数中的 某些之和组成 数字n 可以对 这些数的某一个 进行分解 比如 2^5=32进行一次分解 16 + 16 输出 最小的分解次数,如果没有合适答案 输出-1
问题分析:位运算+贪心
看到 2进制 显然想到的 就是位运算 因为 存在 最小问题,一般就是 贪心之类(当然有时CF故意让你往难的想) 如果是最小问题:就必须把 原本存在的 的2某次方全部减去,这样才能保证最少 显然,for循环 从 2^0到 2^63是无法实现的,因为 你只能 顺讯或者倒叙 统计大小 便于判断不存在的情况 顺序 扫 可能存在 减去大数刚好合适,,减去小数大数就需要分解 倒叙。。。倒是可以处理第一遍,然后从小的往上扫一遍找到第一个大于的 分解 然后 统计数组-- 次级+2然后 再重复操作倒叙 直到。。。。n==0
通过位运算 可以 清楚地知道n二进制每一位 并直接对每位 的直接操作 从最低位 往高位,低位不足 高位补位,低位 冗余 ,高位进位(>>1)
AC代码:
#include<iostream>
#include<cstdio>
#include<cstring>
#include<bitset>
#include<sstream>
#include<string.h>
#include<iomanip>
#include<cmath>
#include<algorithm>
#include<cstdlib>
#include<set>
#include<map>
#include<queue>
#include<vector>
using namespace std
;
#define ll long long
#define lowbit(x) (x)&(-x)
#define mem(a,b) memset((a),(b),sizeof(a));
const ll inf
=0x3f3f3f3f;
typedef pair
<int ,int > PII
;
const int nn
=1e6+10;
ll cnt
[64];
int main()
{
#ifdef io
freopen("in.txt","r",stdin);
#endif
int t
;
cin
>>t
;
while(t
--){
mem(cnt
,0);
ll sum
=0,n
,m
;
cin
>>n
>>m
;
for(int i
=0;i
<m
;i
++){
int x
;
cin
>>x
;
sum
+=x
;
int p
=0;
while((x
>>p
)>1)p
++;
cnt
[p
]++;
}
if(sum
<n
){
cout
<<-1<<endl
;
continue;
}
int idx
=0,ans
=0;
while(n
||cnt
[idx
]<0){
cnt
[idx
]-=(n
&1);
if(cnt
[idx
]<0){
cnt
[idx
+1]--;
ans
++;
}else{
cnt
[idx
+1]+=(cnt
[idx
]>>1);
}
n
>>=1;
idx
++;
}
cout
<<ans
<<"\n";
}
return 0;
}