你有一个大小为n的袋子,同时你有m个盒子。第i个盒子的大小是ai,其中每个ai是2的非负整数次方。
你可以把盒子分成大小相等的两部分。你的目标是完全填满这个袋子。
例如,如果n=10,a=[1,1,32],那么你必须将大小为32的盒子分成大小为16的两部分,然后再将大小为16的盒子分割。所以你可以用大小为1、1和8的盒子装满袋子。
计算填充大小为n的袋子所需的最小除法次数。
输入
第一行包含一个整数t(1≤t≤1000)--测试案例的数量。
每个测试案例的第一行包含两个整数n和m(1≤n≤1018,1≤m≤105)--分别是袋子的大小和盒子的数量。
每个测试案例的第二行包含m个整数a1,a2,...,am(1≤ai≤109)--盒子的大小。保证每个ai是2的幂。
还保证所有测试用例中所有的m之和不超过105。
输出
对于每个测试案例,打印一个整数--填满n大小的袋子所需的最小分割数(如果不可能,则为-1)。
例子
inputCopy
3
10 3
1 32 1
23 4
16 1 4 1
20 5
2 1 16 1 8
输出拷贝
2
-1
0
题意:给你m个数(为2的整数次幂),可以把任意一个数除二,求多少次操作后,其中一些数相加为n
题解:
首先预处理,把所有数都以二进制的形式存储,遍历n的第一位到最后一位,如果n这一位为1,找cnt[i]是有,没有的化,向更高位找,找的过程中,ans++,cnt[i]++,代表高位除二的操作,找到后,本位++,因为分成了两份,为什么高位是+1?因为分解到低位需要当前位减一
当前位-1如果n当前位有1的话
每次遍历玩当前i后要看是否当前为是否大于2大
于二可以合成一个更高位,变相减少了需要的操作
#include<iostream>
#include<cstring>
#include<vector>
using namespace std;
int cnt [70];
int main()
{
int t;
scanf("%d",&t);
while(t--)
{
long long n,m;
memset(cnt,0,sizeof cnt);
long long y = 0;
scanf("%lld%lld",&n,&m);
for(int i = 1;i <= m;i++)
{
long long x;
scanf("%lld",&x);
y += x;
int k = 0;
while(x)
{
x = x/2;
k++;
}
cnt[k-1]++;
}
if(y<n)
{
printf("-1\n");
continue;
}
vector<int> bit;
while(n)
{
bit.push_back(n%2);
n = n/2;
}
int ans =0;
for(int i =0; i < bit.size();i++)
{
if(bit[i])
{
int pos = i;
if(!cnt[pos])
{
while(!cnt[pos])
{
cnt[pos]++;
pos++;
ans++;
}
cnt[pos]--;
cnt[i]++;
}
cnt[i]--;
}
cnt[i+1]+=cnt[i]/2;
}
cout<<ans<<endl;
}
}