CF1303D
题意:现有一背包,容量为n,n ≤ 1018,有m个物品,每个物品占用容量为ai,1 ≤ ai ≤ 109,保证对于每个ai都存在一个非负整数x,使得2x=ai。
现在可以执行任意次如下操作:将ai划分为两个ai/2。
现在要用最少的划分次数,来填满这个背包
思路:显然可以发现,如果对n进行二进制分解,那么我们只需要保证对应二进制位上面为1即可。
所以贪心策略为:对于某一位而言,肯定能用前面的来堆就用前面的,否则就把后面最近的一个分解,使得这一位为11。
实现时我们从低位到高位考虑,某一位考虑完过后,这一位剩下的显然堆在后面最优。如果从高位往低位考虑,则贪心起来十分困难。
#include<bits/stdc++.h>
using namespace std;
typedef long long LL;
const int maxn=100005;
LL a[maxn];
int bit[64];
int main()
{
int T;
scanf("%d",&T);
while(T--){
LL n;
int m;
scanf("%lld%d",&n,&m);
for(int i=0;i<=63;i++){
bit[i]=0;
}
LL sum=0;
LL ans=0;
for(int i=1;i<=m;i++){
scanf("%lld",&a[i]);
sum+=a[i];
int x=a[i]/2,t=0;
while(x){
t++;
x/=2;
}
++bit[t];
}
if(sum<n){
printf("-1\n");
}
else{
for(int i=0;i<=63;i++){
bool need=false;
if(n>>i&1)need=true;
if(bit[i]==0&&need==true){
for(int j=i+1;j<=63;j++){
if(bit[j]>=1){
ans+=(j-i);
for(int k=i;k<j;k++){
++bit[k];
}
++bit[i];
--bit[j];
break;
}
}
}
if(need==true)--bit[i];
bit[i+1]+=(bit[i]/2);//关键的一步,当低位还有剩余,可尽最大努力来贡献给高位
}
printf("%lld\n",ans);
}
}
return 0;
}