CF1303D Fill The Bag 位运算与贪心

传送门:点这里
题意:用所给的的大小为 2 的非负整数次幂的物品把背包装满。
首先我们需要知道一点:物品可以对半拆分。那么这意味什么呢?就是可以把物品拆分成最小的单位,即大小为 1。
于是,我们就可以用这些物品来表示1—sum(物品总和)中的任意一个数的大小,最多只是拆分次数的不同。
思路:先计算出所有物品的总和,如果背包的大小比物品的总和大,显然是不能装满的。接下来就是位运算和贪心了,具体怎么实现请看下面。
首先,我们要把背包装满,而背包的大小是一个数。一个数是可以被拆成二进制数表示的。
例如:样例一中的10,可以拆成1010。

既然背包大小可以被拆成二进制数表示的,物品大小也是如此。
我们把每个物品拆成二进制数,因为物品大小是为 2 的非负整数次幂,所以我们只要找到最高位的 1 就行。
开一个数组来记录物品在二进制位上的数量。

例如:8 大小这个物品,二进制数为 1000,它在第4位上为 1 ,第4位上的数字就代表了有几个8 大小的物品。
接下来就是贪心,其实我更感觉像模拟。
从第一位开始,每次先用物品在这位上数量减去背包在这位上的0或1。如果物品在这位上数量小于 0,从下一位调一个过来(就像减法中借位操作),不管下一位有没有,先减 1再说,但如果物品在这位上数量是大于 2 的,那么我就可以把2个这个位进1下个位了,即有 2 进 1。最后直到 n 是小于0的,但注意n 小于0并不是结束,我们要把位的欠的债还清,结束条件是n 小于0并且该位大于等于0。

代码如下:

#include <bits/stdc++.h>
#define ll long long
#define mem(a,b) memset(a,b,sizeof(a))
#define fo1(a,b) for(int a=0;a<b;++a)
#define fo2(a,b) for(int a=1;a<=b;++a)
#define inf 0x3f3f3f3f
using namespace std;
const int maxn=1e4+5;
const int mod=1e9+7;
int ans[65];
int main()
{
    int t,m,te;
    ll n;
    cin>>t;
    while(t--){
        cin>>n>>m;
        ll sum=0;
        mem(ans,0);
        fo1(i,m){
            scanf("%d",&te);
            sum+=te;
            int p=0;
            while((te>>p)>1)
                p++;
            ans[p]++;
        }
        if(sum<n){
            cout<<-1<<endl;
            continue;
        }
            int index=0,num=0;
            while(n||ans[index]<0){
                ans[index]-=n&1;
                if(ans[index]<0){
                    ans[index+1]--;
                    num++;
                }
                else
                  ans[index+1]+= ans[index]>>1;
                n>>=1;
                index++;
            }
            cout<<num<<endl;
    }
    return 0;
}
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值