题目
问题描述
暗影大帝又开始搞事情了!这次他派出了 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。
题解
思路
-
输入解析:首先,我们读取输入数据,包括铠甲勇士的数量 N 和暗影护法的数量 M,以及每个铠甲勇士的能量值 Ai 和每个暗影护法的战斗力 Bi。
-
能量值统计:使用
Counter
对铠甲勇士的能量值进行统计,这样可以快速知道每种能量值的铠甲勇士有多少个。Counter可以生成{值:出现个数}的字典 -
合体策略:对于每个暗影护法的战斗力 Bi,使用贪心算法从能量值最大的铠甲勇士开始尝试合体。
-
合体过程:
-
对于每个暗影护法的战斗力 Bi,初始化合体个数
count
为 0。 -
遍历所有能量值,从大到小排序。
-
对于每个能量值 e,计算最多可以使用多少个这种能量值的铠甲勇士(即
min(eval // e, energy[e])
),并更新合体个数count
和剩余战斗力eval
。 -
如果在某一步剩余战斗力
eval
为 0,说明已经成功合体,将count
加入结果列表res
并 break 出循环。 -
如果遍历完所有能量值后,剩余战斗力
eval
仍不为 0,说明无法合体成功,将 -1 加入结果列表res
。
-
-
输出结果:最后,将结果列表
res
转换为字符串并输出。
为什么贪心算法一定正确呢?这和铠甲能量互为整数倍这一限制条件有关。
假设目标战斗力为 B,铠甲勇士的能量值为 A1,A2,…,An,且 A1<A2<⋯<An,并且所有能量值之间存在整数倍关系。那么:
-
如果我们从最大的能量值 An 开始尝试,最多可以使用 k=⌊AnB⌋ 个,剩下的部分为 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()
至此,通过所有测试。