刷题笔记 | 英雄决斗的最大胜利次数 | 豆包MarsCode AI刷题

86 阅读4分钟

贪心算法

什么是贪心

贪心的本质是选择每一阶段的局部最优,从而达到全局最优

这么说有点抽象,来举一个例子:

例如,有一堆钞票,你可以拿走十张,如果想达到最大的金额,你要怎么拿?

指定每次拿最大的,最终结果就是拿走最大数额的钱。

每次拿最大的就是局部最优,最后拿走最大数额的钱就是推出全局最优。

再举一个例子如果是 有一堆盒子,你有一个背包体积为n,如何把背包尽可能装满,如果还每次选最大的盒子,就不行了。这时候就需要动态规划。

贪心的套路(什么时候用贪心)

贪心算法并没有固定的套路

所以唯一的难点就是如何通过局部最优,推出整体最优。

那么如何能看出局部最优是否能推出整体最优呢?有没有什么固定策略或者套路呢?

不好意思,也没有! 靠自己手动模拟,如果模拟可行,就可以试一试贪心策略,如果不可行,可能需要动态规划。

有同学问了如何验证可不可以用贪心算法呢?

最好用的策略就是举反例,如果想不到反例,那么就试一试贪心吧

可有有同学认为手动模拟,举例子得出的结论不靠谱,想要严格的数学证明。

一般数学证明有如下两种方法:

  • 数学归纳法
  • 反证法

看教课书上讲解贪心可以是一堆公式,估计大家连看都不想看,所以数学证明就不在我要讲解的范围内了,大家感兴趣可以自行查找资料。

面试中基本不会让面试者现场证明贪心的合理性,代码写出来跑过测试用例即可,或者自己能自圆其说理由就行了

举一个不太恰当的例子:我要用一下1+1 = 2,但我要先证明1+1 为什么等于2。严谨是严谨了,但没必要。

虽然这个例子很极端,但可以表达这么个意思:刷题或者面试的时候,手动模拟一下感觉可以局部最优推出整体最优,而且想不到反例,那么就试一试贪心

例如刚刚举的拿钞票的例子,就是模拟一下每次拿做大的,最后就能拿到最多的钱,这还要数学证明的话,其实就不在算法面试的范围内了,可以看看专业的数学书籍!

所以这也是为什么很多同学通过(accept)了贪心的题目,但都不知道自己用了贪心算法,因为贪心有时候就是常识性的推导,所以会认为本应该就这么做!

问题描述

小U和小F正在进行一场由 nn 轮组成的英雄决斗比赛。在每一轮中,小U和小F各自从他们的英雄队伍中选出一位英雄进行对决,英雄的能力值将决定比赛的胜负,能力值高者获胜。小U已经按照固定的能力顺序 1,2,3,…,n1,2,3,…,n 安排了他的英雄出场顺序。

小F希望通过调整他的英雄出场顺序,最大化他的获胜轮数。请帮助小 F 确定一个最佳的出场顺序,以获得最多的胜利。

输入说明

  • number: 一个整数,表示比赛的总轮数 nn。
  • heroes: 一个长度为 nn 的正整数数组,表示小 F 的每个英雄的能力值。

输出

  • 返回一个整数,表示小 F 可以获得的最大胜利轮数。

测试样例

样例1:

输入:number = 7, heroes = [10, 1, 1, 1, 5, 5, 3]
输出:4

样例2:

输入:number = 5, heroes = [1, 1, 1, 1, 1]
输出:0

样例3:

输入:number = 6, heroes = [9, 4, 7, 3, 2, 6]
输出:6

解题思路

  1. 排序:首先,我们可以对小F的英雄能力值进行排序。这样我们可以更容易地找到一个最佳的出场顺序。
  2. 贪心策略:从小F的英雄中选择一个能力值刚好大于小U当前英雄的能力值的英雄进行对决。这样可以最大化小F的获胜轮数。
import java.util.Arrays;

public class Main {
    public static int solution(int number, int[] heroes) {
        // 对小F的英雄能力值进行排序
        Arrays.sort(heroes);
        
        // 初始化胜利轮数
        int wins = 0;
        
        // 使用双指针进行贪心选择
        int fIndex = 0; // 指向小F的英雄
        for (int uValue = 1; uValue <= number; uValue++) { // 小U的英雄能力值从1到n
            // 找到一个能力值刚好大于uValue的小F的英雄
            while (fIndex < number && heroes[fIndex] <= uValue) {
                fIndex++;
            }
            
            // 如果找到了合适的英雄,则小F获胜
            if (fIndex < number) {
                wins++;
                fIndex++; // 移动到下一个英雄
            } else {
                break; // 如果没有合适的英雄,结束循环
            }
        }
        
        return wins;
    }

    public static void main(String[] args) {
        //  You can add more test cases here
        int[] heroes1 = {10,1,1,1,5,5,3};
        int[] heroes2 = {1,1,1,1,1};
        int[] heroes3 = {1,2,3,4,5,6,7,8,9,10};

        System.out.println(solution(7, heroes1) == 4);
        System.out.println(solution(5, heroes2) == 0);
        System.out.println(solution(10, heroes3) == 9);
    }
}