“田忌赛马”——英雄决斗的最大胜利次数(中等题)
小U和小F正在进行一场英雄决斗比赛,该比赛一共有n轮次。在每一轮中,小U和小F分别从他们的英雄队伍中选出一位英雄进行对决,英雄的能力值决定比赛的胜负,能力值高者获胜。小U已经按照固定能力值顺序1, 2, 3, 4, ,5......n安排了他的英雄出场顺序。小F希望通过调整他的英雄出场顺序,最大化他的获胜轮数,请帮助小F安排他的英雄出场顺序。
输入:
number: 一个整数,表示比赛的总轮数 n
heroes: 一个长度为 n 的整数数组,表示小F每个英雄的能力值
输出:
返回一个整数,表示小F可以获得的最大胜利轮数
样例
输入: number = 7, heroes = [10, 1, 1, 1, 5, 5, 3], 输出: 4
关键问题分析:(以下解释中能力值用值代替)
这是一个“田忌赛马”的问题,也就是要在小U的每轮英雄的值分别是1, 2, 3, 4, ,5......n的情况下,小F的第i轮尽量安排值大于i的英雄。这里有两个小坑:
(1)不能随便挑出一个值比i大的英雄参与第i轮比赛,否则不能胜利次数最大化。举个反例:如样例中所示,第1、2、3轮小U的英雄值分别为1、2、3,如果小F选择值为5、5、10的英雄出战,虽然可以赢得这两轮,但是第4轮的时候没有英雄可以获胜;
(2)可不可以直接对小F的heroes数组排序,然后计算第i个英雄值大于i的个数?举个反例:依然看样例,排序后对比各自的英雄值,可以直观的看出,这样只能赢下两轮,显然不能这么安排。
小F heroes = [1, 1, 1, 3, 5, 5, 10]
小U heroes = [1, 2, 3, 4, 5, 6, 7]
思路解析:
其实问题很简单,可以简化为:在长度为n的heroes数组中,能找出多少个比数组[1, 2, 3...n]
中任意一个值大的数,且各自数组中的每个数只能用一次。找到heroes数组中值比小U的英雄值大的数很容易,关键在于:(1)对比英雄值时如何判断小U的英雄是否已经出战过;(2)如何最大化所谓的“获胜轮数”。
对于第一个关键点,我们可以用一个 visited 数组来存放已经小 U 已经使用过的英雄,每轮比赛时判断出战的英雄是否已经存在于 visited 数组中即可;关于第二点,只要对小F的每一个英雄,都从小U值为1的英雄开始比较,直到找到没有出战过的英雄,即可最大化胜利次数。
def solution(number, heroes):
heroes.sort() # 先对小F的英雄进行排序
visited = [] # 存放小U已经使用过的英雄
for item in heroes:
i = 1
# 从小U值为1的英雄开始比较,直到找到一个没有出战过的英雄
while i < item:
#dif = item - i
if i not in visited:
visited.append(i)
break
else:
i += 1
return len(visited)
延伸:如果小U的英雄值并不是简单的1,2,3,4,......n呢?应该怎么办?
其实道理是一样的,假设小U的英雄值分别是a1,a2,a3,......an,我们只要每次都从英雄值相差为1开始找起即可,假设小U和小F分别有以下英雄,
小 U: 5 7 6 8 5
小 F: 9 6 8 5 8
对小U和小F的英雄排序后,按照值相差为1并依次增加的原则开始找起,总能满足最大获胜次数。例如与小差1的小U英雄是7,对战完之后,小F下一个值为8的英雄再次从相差1开始找起,而小U的英雄7已经出战过,那么差值+1继续寻找,则找到了小U的英6。
小 U: 5 6 7 8
小 F: 5 6 8 8