Ray's blog

Code, Life and Thoughts

0%

advantage shuffle

最近碰到一道很有意思的题目, 叫作优势排序:

advantageshuffle

一句话来总结就是说A和B手里有一组牌,每轮他们都可以分别出一牌进行PK,假设B的出牌顺序不变,那么A什么样的出牌顺序能让A最终的pk的优势最大?

仔细想想,我们可以用这样的策略:A用自己最小的牌a和B最小的牌b进行比较,如果a>b, 我们就用a来匹配b,因为此时A中剩余的其他牌都要比b大,使用最弱的牌a可以让我们剩余的牌严格变大,这样A有更多的优势去赢B。如果a<b, 那么我们用a去匹配B中最大的牌,削弱B的最强的顶尖力量,这样A中剩余的牌也有更多的机会去赢B。

等等,这样的策略是不是有点耳熟,这不就是我们小学课本中学过的田忌赛马的策略吗?

齐国的大将田忌很喜欢赛马。有一回他和齐威王约定,进行一次比赛。
他们把各自的马分成上、中、下三等。比赛的时候,上等马对上等马,中等马对中等马,下等马对下等马。由于齐威王每个等级都比田忌的强,三场比下来,田忌都失败了。田忌觉得很扫兴,垂头丧气地准备离开赛马场。
这时,田忌发现,他的好朋友孙膑也在人群里。孙膑招呼田忌过来,拍着他的肩膀,说:“从刚才的情形看,齐威王的马比你的马快不了多少呀……”
孙膑还没说完,田忌瞪了他一眼,说:“想不到你也来挖苦我!”
孙膑说:“我不是挖苦你,你再同他赛一次,我有办法让你取胜。”
田忌疑惑地看着孙膑:“你是说另换几匹马?”
孙膑摇摇头,说:“一匹也不用换。”
田忌没有信心地说:“那还不是照样输!”
孙膑胸有成竹地说:“你就照我的主意办吧。” 齐威王正在得意洋洋地夸耀自己的马,看见田忌和孙膑过来了,便讥讽田忌:“怎么,难道你还不服气?”
田忌说:“当然不服气,咱们再赛一次!”
齐威王轻蔑地说:“那就来吧!”
一声锣响,赛马又开始了。
孙膑让田忌先用下等马对齐威王的上等马,第一场输了。
接着进行第二场比赛。孙膑让田忌拿上等马对齐威王的中等马,胜了第二场。齐威王有点儿心慌了。
第三场,田忌拿中等马对齐威王的下等马,又胜了一场。这下,齐威王目瞪口呆了。
比赛结果,田忌胜两场输一场,赢了齐威王。
还是原来的马,只调换了一下出场顺序,就可以转败为胜。`

再次回顾一下,如果我们把B中牌的顺序当成齐威王的马匹出场顺序,B中每个元素当成齐威王的马匹能力,那么问题就变成,如何调整田忌(A)的出马顺序,能让赢的场次更多?

我们第一步,将齐威王的马和田忌的马按照能力值先进行从大到小排序,同时我们记录齐威王的马的原来的出场顺序。
第二步,我们用田忌的最弱的马和齐威王最弱的马进行比较,如果田忌的马强,就让这两匹马进行比赛,如果田忌的马弱,则让该马去和齐威王最强的马进行比赛。

写成python代码如下:

def advantageCount(self, A: List[int], B: List[int]) -> List[int]:
    #用来保存A马匹的最终pk顺序
    res = [None] * len(A)
    #保存排序B马匹,并记录每个马的原始位置
    B = collections.deque(sorted([(b, i) for i,b in enumerate(B)]))
    #从小到大遍历A马匹
    for i in sorted(A):
        #如果当前A中最弱的马比B中最弱的马强,则使用该马进行pk,将此马记录到B对应的位置上去
        if i > B[0][0]:
            res[B.popleft()[1]] = i
        #如果当前A中最弱的马比B中最弱的马弱,使用该马pkB中最强的马,并进行记录。
        else:
            res[B.pop()[1]] = i
    return res