• Codeforces 1303D Fill The Bag


    Description

    描述

    你有一个大小为 $n$ 的背包和 $m$ 个大小为 $2$ 的非负整数次幂的物品,其中第 $i$ 个物品的大小为 $a_i$。

    每次可以把一个物品划分成两个相同大小的物品。你需要恰好填满整个背包。

    例如,当 $n = 10$ 且 $a = [1,1,32]$ 时,你可以把大小为 $32$ 的物品划分成两个大小为 $16$ 的物品,再把其中一个大小为 $16$ 的物品划分成两个大小为 $8$ 的物品,这样你就可以用两个大小为 $1$ 的物品和一个大小为 $8$ 的物品填满这个背包。

    求出最小的划分物品的次数。

    输入

    第一行一个正整数 $T$,表示 $T$ 组数据。

    每组数据的第一行为两个正整数 $n$($1le n le 10^{18}$)和 $m$($1 le m le 10^5$)。

    接下来为 $m$ 个正整数 $a_i$($1 le a_i le 10^9$)。

    输出

    每组数据一个数表示最小的划分物品的次数,如果无法实现输出 $-1$。

    样例

    输入

    3
    10 3
    1 32 1
    23 4
    16 1 4 1
    20 5
    2 1 16 1 8

    输出

    2
    -1
    0

    Solution

    我们可以按位贪心。

    用 $c_i$ 表示 $a$ 数组中 $2^i$ 出现的个数,记 $sum = sumlimits_{i=1}^{m} a_i$,显然,有解的充要条件是 $sum ge n$,反之无解。

    我们从 $2^0 o 2^{29}$ 贪心,比如现在在考虑 $2^i$ 这一位,如果 $n operatorname{and} 2^i=0$($n$ 不含这一位) ,那显然是没那个必要考虑的。如果是 $1$ 的话,那么用 $tmp$ 来表示用 $a$ 数组中 $2^0 sim 2^{i - 1}$ 能拼出来多少个 $2^i$。如果 $tmp ge 1$ 的话,直接 $tmp gets tmp - 1$ 就行了,表示消耗一个;如果 $tmp = 0$,那我们显然只能借助于 $2^i sim 2^{29}$ 来完成这个操作了,这时可以暴力的用 $j$ 从 $i o 29$ 枚举,找到第一个 $c_j ge 1$ 的,把 $c_j gets c_j - 1$,然后把 $c_i sim c_{j - 1}$ 都增加 $1$,同时 $ans$ 要累加上 $j - i$(从 $2^j$ 除到 $2^i$ 就是 $j - i$ 次),别忘了 $tmp$ 要变成 $0$。最后按照 $tmp$ 的定义,当我们考虑 $2^{i+1}$ 时,$2^i$ 显然是要累加进去的,所以 $tmp = frac{tmp + c_i}{2}$。

    #include <bits/stdc++.h>
    using namespace std;
    typedef long long LL; 
    const int M = 100005;
    LL n, m, a[M], ans;
    int c[50];
    int main()
    {
    	ios::sync_with_stdio(false);
    	int t;
    	cin >> t;
    	while(t--)
    	{
    		ans = 0;
    		LL sum = 0;
    		memset(c, 0, sizeof c); 
    		cin >> n >> m;
    		for(int i = 1; i <= m; i++) cin >> a[i];
    		for(int i = 1; i <= m; i++)
    		{
    			int k = 0;
    			sum += a[i];
    			while(a[i] > 1) k++, a[i] >>= 1;
    			c[k]++;
    		}
    		if(sum < n)
    		{
    			cout << "-1
    ";
    			continue;
    		}
    		LL tmp = 0;
    		for(int i = 0; i < 30; i++)
    		{
    			if(n >> i & 1) tmp--;
    			if(tmp < 0)
    			{
    				int j;
    				for(j = i; !c[j]; j++) c[j] = 1;
    				c[j]--;
    				ans += j - i;
    				tmp = 0; 
    			}
    			tmp = tmp + c[i] >> 1;
    		}
    		cout << ans << endl;
    	}
    	return 0;
    }
  • 相关阅读:
    Linux shell脚本基础学习详细介绍(完整版)二
    python读取单个文件操作
    【转载】HTTP 缓存的四种风味与缓存策略
    【转载】HTTP 响应头与状态码
    【转载】HTTP 请求头与请求体
    【转载】HTTP 基础与变迁
    3-2 从单词中获取单词出现的频率信息,并把他们写进对应的列表里
    170925_2 Python socket 创建UDP的服务器端和客户端
    170925_1 Python socket 创建TCP的服务器端和客户端
    2-2 列表推导同 filter 和 map 的比较
  • 原文地址:https://www.cnblogs.com/syksykCCC/p/CF1303D.html
Copyright © 2020-2023  润新知