D. Fill The Bag(二进制+贪心)

Problem - 1303D - Codeforces

你有一个大小为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;
		
	}
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值