codeforces 1303D Fill The Bag 贪心

https://vjudge.net/problem/CodeForces-1303D
在这里插入图片描述题目大意:你有一个大小为 n n n的背包, m m m个盒子,第 i i i个盒子的大小为 a i a_i ai,保证 a i a_i ai均为 2 2 2的幂次,每次操作可以选取任意一个盒子并把它分成两个大小相等的盒子,问经过多少次操作后你可以用其中一部分盒子填满背包,若无解输出 − 1 -1 1

思路:贪心,考虑 n n n的二进制的第 i i i位,要么由现在已经有的盒子凑出 2 i 2^i 2i,要么划分 a j > 2 i a_j>2^i aj>2i的盒子,因为前者对答案没有贡献,所以优先使用这种方法,如果凑不出来的话只能采用后者,为了使答案最小我们肯定要找到 > 2 i >2^i >2i且最小的 a j a_j aj进行划分;如果这两种方法都不可行的话,说明无解。

#include<bits/stdc++.h>
#define INF 0x3f3f3f3f
using namespace std;
typedef long long ll;

const int maxn=1e5+5;

ll n;
int t,m,a[maxn],cnt[70];
//cnt[i]记录大小为2^i的盒子的个数
int main()
{
    scanf("%d",&t);
    while(t--)
    {
        memset(cnt,0,sizeof(cnt));
        ll sum=0;
        scanf("%lld%d",&n,&m);
        for(int i=0;i<m;i++)
        {
            scanf("%d",&a[i]);
            sum+=a[i];
        }
        if(sum<n)
            printf("-1\n");
        else if(sum==n)
            printf("0\n");
        else
        {
            int ans=0;
            bool flag=1;
            for(int i=0;i<m;i++)
                cnt[(int)(log2(a[i])+0.5)]++;
            for(ll i=1,j=0;i<=n&&flag;i<<=1,++j)
            {
                if(n&i)
                {
                    if(cnt[j])//用已经有的盒子
                        --cnt[j];
                    else
                    {
                        bool tag=0;
                        for(int k=j+1;k<60;k++)//找到一个盒子进行划分
                        {
                            if(cnt[k])
                            {
                                tag=1,--cnt[k],ans+=k-j;
                                while(--k>j)
                                    ++cnt[k];
                                break;
                            }
                        }
                        flag=tag;
                    }
                }
                cnt[j+1]+=cnt[j]>>1;//两个小盒子合成一个大盒子
            }
            if(!flag)
                ans=-1;
            printf("%d\n",ans);
        }
    }
    return 0;
}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值