【CF】D. Fill The Bag 二进制

https://codeforces.com/problemset/problem/1303/D

D. Fill The Bag

题目:

思路:

题意就是给你m个数,让你用这m个数拼成n,对于任意一个m,只要其大于1,那么就可以对齐进行拆分操作,这样就能得到两个 x/2

接下来我们想,题目所给的条件是每个 a[i] 都是 2 的x次幂,然后我们又要将 a[i] 拼成一个数,那我们就可以考虑使用二进制

那么对于任意一个数我们将其转化为二进制形式,如果该处是0,说明不需要考虑,否则说明只需要一个2的x次幂即可,所以我们用一个map来存储到底有多少个2的x次幂,这里我们可以直接存log(a[i]),因为到时候我们可以使用位运算来判断该位是0还是1,那对于第i位,我们只需要2的i次幂即可,即从map中寻找是否有i

接下来我们考虑如果没有对应的i呢?显然我们要找一个比i大数来拆分,假如我们需要2^{a},但是目前我们没有,那我们就要往上寻找,一直找到第一个map值大于1的b,即2^{b}

那么我们一旦拆开就可以要拆成 2^{b-1} + 2^{b-1} \rightarrow 2^{b-1} +2^{b-2} + 2^{b-2} \rightarrow ...

一直到拆成2^{a},那么拆分的次数就是b-a了,同时我们发现,每次拆解都多了一个2^{b-i},也就是说从a~b每一个都有了,那我们下一次是就可以直接到b的位置往后搜索了,特别的,如果当前有剩余的话,我们可以将下一位加上当前剩余的除以2,如1+1=2(对应2的1次幂),2+2=4(对应2的2次幂),那就能最大化利用了

同时注意,如果sum小于n的话,我们肯定不能构成,其他肯定可以,因为最后都能拆成1

代码:

#include <iostream>
#include <algorithm>
#include<cstring>
#include<cctype>
#include<string>
#include <set>
#include <vector>
#include <cmath>
#include <queue>
#include <unordered_set>
#include <map>
#include <unordered_map>
#include <stack>
#include <memory>
using namespace std;
#define ll long long
#define yes cout << "YES" << endl
#define no cout << "NO" << endl

void solve()
{
    ll n, m;
    cin >> n >> m;
    vector<ll> a(m);
    ll sum = 0;
    map<int, int> c;
    for (int i = 0; i < m; i++)
    {
        cin >> a[i];
        sum += a[i];
        c[log2(a[i])]++;
    }
    if (sum < n)
    {
        cout << -1 << endl;
        return;
    }
    int i = 0, res = 0;
    while (i < 60) {
        if ((n >> i) & 1) { // 检查 n 的第 i 位是否为 1
            if (c[i] > 0) {
                c[i]--;
            }
            else {
                while (i < 60 && c[i] == 0) {
                    i++;
                    res++;
                }
                c[i]--;
                continue;
            }
        }
        c[i + 1] += c[i] / 2;
        i++;
    }
    cout << res << endl;
}

int main()
{
    cin.tie(0)->sync_with_stdio(false);
    int t = 1;
    cin >> t;
    while (t--)
    {
        solve();
    }
    return 0;
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值