vlambda博客
学习文章列表

604,贪心算法解优势洗牌-田忌赛马问题

问题描述



来源:LeetCode第870题

难度:中等


给定两个大小相等的数组A和B,A相对于B的优势可以用满足A[i]>B[i]的索引i的数目来描述。


返回A的任意排列,使其相对于B的优势最大化。


示例 1:

输入:A = [2,7,11,15], B = [1,10,4,11]

输出:[2,11,7,15]

示例 2:

输入:A = [12,24,8,32], B = [13,25,32,11]

输出:[24,32,8,12]


提示:

  • 1 <= A.length = B.length <= 10000

  • 0 <= A[i] <= 10^9

  • 0 <= B[i] <= 10^9


问题分析



这题说的是在同一位置如果数组A中的元素大于数组B中的元素,那么数组A就具有一个优势,我们需要对数组A重新排列,让数组A相对于数组B的优势最大。


如果数组的长度是3,这就是经典的田忌赛马问题,大家也都知道该怎么解决。但如果数组的长度不是3,而是100,1000我们该怎么解决呢。这里我们还按照田忌赛马的问题来解决。


数组A我们看作是田忌的马,数组B我们看作是齐王的马,数组中的值表示马跑的快慢。

  • 如果田忌最快的马还没有齐王最快的马跑的快,就用田忌最慢的马齐王最快的马比赛。

  • 如果田忌最快的马比齐王最快的马跑的快,就用田忌最快的马齐王最快的马比赛。


因为是一对一的比赛,第一种方案我们都能理解,因为齐王最快的马比田忌最快的马跑的还快也可以认为比田忌所有的马都快),这时候田忌无论用哪匹马和齐王比赛都会输,既然输,不如用田忌最慢的马和齐王最快的马比赛好了。


对于第二个方案,如果田忌最快的马跑的比齐王最快的马还要快,我们能不能用田忌第二快的马和齐王最快的马比较呢,其实没必要的。

  • 如果田忌第二快的马跑的比齐王最快的马还要快,也就是说田忌最快的前两匹马和齐王的任何一匹马比赛都能获胜,我们就用田忌最快的马和齐王最快的马比赛就行了。

  • 如果田忌第二快的马没有齐王最快的马跑的快,我们只能用田忌最快的马和齐王最快的马比赛。

所以不管田忌第二快的马有没有齐王最快的马跑的快,当田忌最快的马跑的比齐王最快的马还要快的时候,我们就用田忌最快的马和齐王最快的马比赛就行了。


原理也比较简单,我们来看下代码

public int[] advantageCount(int[] nums1, int[] nums2) {
    int length = nums1.length;
    int[] res = new int[length];
    //先对数组nums1从小到大进行排序
    Arrays.sort(nums1);
    //对数组nums2从大到小排序,这里使用的是优先队列,也就是最大堆,每次出队
    //的都是堆中最大的元素。这里存储的是数组nums2中的元素和元素对应的下标
    PriorityQueue<int[]> pq = new PriorityQueue<>((a, b) -> b[0] - a[0]);
    //数组nums2中的元素和元素对应的下标加入到队列中
    for (int i = 0; i < length; i++)
        pq.add(new int[]{nums2[i], i});
    //双指针,分别指向数组nums1中的最小值和最大值。可以类比于田忌跑的
    //最慢的马和跑的最快的马
    int left = 0;
    int right = length - 1;
    while (!pq.isEmpty()) {
        //队列中存放的是数组nums2中的元素,每次出队的都是堆中最大的值,
        //类比齐王每次都拿出剩下的马中最好的马比赛
        int[] cur = pq.poll();
        int index = cur[1];
        int val = cur[0];
        //先用nums1中的最大值和nums2中的最大值比较,类似于田忌用最好的马
        //和齐王最好的马比较,如果田忌最好的马跑的比齐王最好的马跑的快,
        //就用田忌最好的马和齐王最好的马比赛
        if (nums1[right] > val) {
            res[index] = nums1[right--];
        } else {
            //如果田忌最好的马没有齐王最好的马跑的快,就用田忌最差的马
            //和齐王最好的马比赛
            res[index] = nums1[left++];
        }
    }
    return res;
}


田忌赛马的故事



文言文:

齐使者如,孙膑以刑徒阴见,说齐使。齐使以为奇,窃载与之齐。齐将田忌善而客待之。忌数与齐诸公子驰逐重射。孙子见其马足不甚相远,马有上、中、下辈。于是孙子谓田忌曰:“君弟重射,臣能令君胜。”田忌信然之,与王及诸公子逐射千金。及临质,孙子曰:“今以君之下驷与彼上驷,取君上驷与彼中驷,取君中驷与彼下驷。”既驰三辈毕,而田忌一不胜而再胜,卒得王千金。于是忌进孙子于威王。威王问兵法,遂以为师。


翻译:

齐国使者到大梁来,孙膑以刑徒的身份秘密拜见,劝说齐国使者。齐国使者觉得此人是个奇人,就偷偷地把他载回齐国。齐国将军田忌非常赏识他,并且待如上宾。田忌经常与齐国众公子赛马,设重金赌注。孙膑发现他们的马脚力都差不多,马分为上、中、下三等,于是对田忌说:“您只管下大赌注,我能让您取胜。”田忌相信并答应了他,与齐王和各位公子用千金来赌注。比赛即将开始,孙膑说:“现在用您的下等马对付他们的上等马,用您的上等马对付他们的中等马,用您的中等马对付他们的下等马。”三场比赛结束后,田忌一场败而两场胜,最终赢得齐王的千金赌注。因此田忌把孙膑推荐给齐威王。齐威王向他请教了兵法,于是把他当成老师。


翻译:

1.如:往,到......去

2.梁:魏国的都城

3.数:屡次,多次

4.公子:春秋战国时诸侯不能继承君位的儿子称为公子

5.驰逐:驾马车竞逐

6.重射:下很大赌注赌输赢。射:打赌。

7.这里指战国时齐国人孙膑,是春秋末期著名军事家孙武(著有《孙子兵法》)的后世子孙,生卒年不可考。1972年,从山东临沂银雀山一座西汉墓葬中发现了失传一千多年的《孙膑兵法》

8.马足:指马的足力

9.不甚相远:不是相差的很远

10.辈:类,这里指马的不同等级

11.弟:只管

12.然:认为......是正确的 然之:即以之为然

13.逐射千金:下千金的赌注赌驾马车比赛的输赢

14.及:等到

15.临质:将要比赛的时候。质,评判,这里指比赛。

16.驷:古代驾车,一车四马,同驾一辆车的马叫做驷

17.既:已经。这里指三种等级的马驾车比赛结束

18.一不胜而再胜:一次没能取胜,但是后两次都取得了胜利。再:两次。

19.卒:最后

20.遂:于是。




你点的每个赞,我都认真当成了喜欢