小M的光明之魂速通挑战
问题描述
小M想知道,在不违反规则的前提下,他最多能够击败多少个Boss。
规则如下:
- 每个Boss对应唯一的类型编号。
- 每把武器能够击杀两种编号的boss且只能使用一次。
- 必须按照给定的顺序击杀Boss。
输入拥有的武器数量n 、需要击败的Boss数量m、Boss的顺序boss:长度为m的list、可以使用的武器array:一个长度为n的二维列表,每个元素为 [x, y],可以击败编号为 x 和 y 的Boss。
输出最多击败多少个boss。
问题分析
本题我们需要解决两个问题:
- 每次是否能够击败当前的boss
- 选哪把武器能打倒之后更多的boss
对于问题1,我们很好处理,我们只需要遍历所有的武器,找到能解决这个boss的武器即可。
而对于问题2,为了能击败更多的boss,我们就需要考虑如何从能打到boss的武器中挑一把对之后打倒更多boss影响最小的武器。
由于原本问题中,我们输入的boss编号并不是从1到m这样子排序的,输入的boss可能很乱,这很不利于计算每把可以选择的武器的优先级。因此我们做出一个大胆的举动,我们将boss的编号连同武器对应的编号都改为按照boss出场顺序的编号。
for i in range(m):
for j in range(n):
if array[j][0] == boss[i]:
array[j][0] = i
if array[j][1] == boss[i]:
array[j][1] = i
我们甚至发现在这样修改之后我们不需要再使用boss这个数组了,我们使用range(m)可以起到同样的用途。
接着我们讨论如何遍历所有武器找到最适合打倒boss的武器。首先我们需要先将所有可以打倒当前boss的武器放入一个数组中。同样的,当没有武器可以打倒这个boss时,数组为空,这样也可以作为我们输出结果的依据。
for i in range(m):
weapon_list = []
for j in range(len(array)):
if array[j][0] == i or array[j][1] == i:
weapon_list.append(j)
if len(weapon) == 0:
return i
接着我们考虑如何选取最适合的武器,我们希望我们尽量不要选择能够打倒之后会出现的boss的武器,也就是说,同时也能够打倒在当前boss出现之前的boss的武器最优先。接着,若是不存在这种武器,我们希望选择一把对之后打boss影响最小的武器。或许你想到了此时应该用递归法来解这个题目,因为很明显,这个题目用递归法讨论所有情况时必然会找到最优解。由于它的性能并不太高,我决定在没有找到同时也能够打倒在当前boss出现之前的boss的武器这种情况下再使用递归法。
回溯法的思想很简单,即递归的思想,找到所有情况中打倒boss最大的值,加上一后传回上一层调用它的递归函数里。 而如何递归,我们决定对所有可以使用的武器都讨论一遍,选取最后能打倒最多boss的情况即可。
我们令当前的boss为,函数使用的boss记作为,可以打倒的武器的集合为,所有的武器集合为,此时我们要调用的函数就是
find_best = False
best_index = -1
for j in range(len(weapon_list):
if array[weapon_list][0] < i or array[weapon_list][1] < i:
find_best = True
best_index = weapon_list[j]
if find_best:
# 我们使用这种武器时,去掉当前boss的boss数组和去掉这个武器的array数组
array = [...]
boss = [...]
return 1+solution(n-1, m-1, boss, array)
else:
# 我们将讨论去掉任何一个weapon_list后可能的结果
ans_list = []
boss = [...]
for j in range(weapon_list):
array = [...]
ans_list.append(solution(n-1, m-1, boss, array)
return 1+max(ans_list)
借助递归的方法,我们成功得到了所有的答案。