【蓝桥杯】铠甲合体

题目

问题描述

暗影大帝又开始搞事情了!这次他派出了 M 个战斗力爆表的暗影护法,准备一举摧毁 ERP 研究院!M 个暗影护法的战斗力可分别用 B1,⋯,BM表示。

ERP 研究院紧急召唤了 N位铠甲勇士前来迎战!每位铠甲勇士都拥有强大的能量,能量值分别为 A1,⋯,AN。这些能量值之间存在着某种特殊的联系:任意两位铠甲勇士的能量值,其中一个总是另一个的整数倍。

例如,可能存在能量值分别为 1,2,4,8的铠甲勇士,但绝不会出现能量值分别为 2 和 3 的铠甲勇士。

为了击败暗影护法,铠甲勇士们需要进行合体,将自身的能量组合起来。当合体后的能量总和恰好等于护法的战斗力时,就能将其击败。 现在,ERP 研究院需要你的帮助!对于每个暗影护法,请你计算出需要多少个铠甲勇士合体才能击败他。如果无论如何都无法击败,那就暂时撤退!

输入格式

第一行输入两个整数 N 和 M (1≤N,M≤10E5),分别表示铠甲勇士的数量和暗影护法的数量。

第二行输入 N 个整数 A1,A2,…,AN​ (1≤Ai≤10E10),表示每个铠甲勇士的能量值。

第三行输入 M 个整数 B1,B2,…,BM (1≤Bi≤10E10),表示每个暗影护法的战斗力。

输出格式

输出一行,包含 MM 个整数,分别表示击败每个暗影护法所需的最少合体个数;如果无法击败则输出 −1。

样例输入

3 2
2 2 2
6 3

样例输出

3 -1

样例解释

对于战斗力为 6 的暗影护法,可以由三个能量值为 2的铠甲勇士合体击败,最少合体个数为 3。

对于战斗力为 3 的暗影护法,无法由任何铠甲勇士组合击败,因此输出 -1。


题解 

思路

  1. 输入解析:首先,我们读取输入数据,包括铠甲勇士的数量 N 和暗影护法的数量 M,以及每个铠甲勇士的能量值 Ai​ 和每个暗影护法的战斗力 Bi​。

  2. 能量值统计:使用 Counter 对铠甲勇士的能量值进行统计,这样可以快速知道每种能量值的铠甲勇士有多少个。Counter可以生成{值:出现个数}的字典

  3. 合体策略:对于每个暗影护法的战斗力 Bi​,使用贪心算法从能量值最大的铠甲勇士开始尝试合体。

  4. 合体过程

    • 对于每个暗影护法的战斗力 Bi​,初始化合体个数 count 为 0。

    • 遍历所有能量值,从大到小排序。

    • 对于每个能量值 e,计算最多可以使用多少个这种能量值的铠甲勇士(即 min(eval // e, energy[e])),并更新合体个数 count 和剩余战斗力 eval

    • 如果在某一步剩余战斗力 eval 为 0,说明已经成功合体,将 count 加入结果列表 res 并 break 出循环。

    • 如果遍历完所有能量值后,剩余战斗力 eval 仍不为 0,说明无法合体成功,将 -1 加入结果列表 res

  5. 输出结果:最后,将结果列表 res 转换为字符串并输出。

为什么贪心算法一定正确呢?这和铠甲能量互为整数倍这一限制条件有关。

假设目标战斗力为 B,铠甲勇士的能量值为 A1​,A2​,…,An​,且 A1​<A2​<⋯<An​,并且所有能量值之间存在整数倍关系。那么:

  • 如果我们从最大的能量值 An​ 开始尝试,最多可以使用 k=⌊An​B​⌋ 个,剩下的部分为 B′=B−k×An​。

  • 减少An的数量只有可能增加需要的铠甲数。由于 An​ 是其他能量值的倍数,在k以内不存在用更多的An不能正好达到某数,而更少的An可以的情况。

  • 若B可以被刚好达到,B‘一定也可以

在这个问题中,“当前最优”就是尽可能用能量值最大的铠甲勇士去合体,因为这样可以快速减少目标值的规模。而整数倍关系的约束保证了这种贪心选择不会错过最优解。

代码

from collections import Counter


def main():
    n, m = map(int, input().split())
    energy = Counter(map(int, input().split()))
    evals = list(map(int, input().split()))
    res = []
    for eval in evals:
        count = 0
        for e in sorted(energy.keys(), reverse=True):
            use_count = min(eval // e, energy[e])
            count += use_count
            eval -= use_count * e
            if eval == 0:
                res.append(count)
                break
        if eval != 0:
            res.append(-1)
    print(' '.join([str(x) for x in res]))
if __name__=='__main__':
    main()

至此,通过所有测试。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值