[题目解析]数学专题:20、44、52、78、100 | 豆包MarsCode AI刷题

1,032 阅读8分钟

20.比赛配对问题

问题描述

小R正在组织一个比赛,比赛中有 n 支队伍参赛。比赛遵循以下独特的赛制:

  • 如果当前队伍数为 偶数,那么每支队伍都会与另一支队伍配对。总共进行 n / 2 场比赛,且产生 n / 2 支队伍进入下一轮。
  • 如果当前队伍数为 奇数,那么将会随机轮空并晋级一支队伍,其余的队伍配对。总共进行 (n - 1) / 2 场比赛,且产生 (n - 1) / 2 + 1 支队伍进入下一轮。

小R想知道在比赛中进行的配对次数,直到决出唯一的获胜队伍为止。

测试样例

样例1:

输入:n = 7 输出:6

样例2:

输入:n = 14 输出:13

样例3:

输入:n = 1 输出:0

解决方案

按照题意进行模拟即可,如果n为偶数,那么n变成原来的一半;如果n为奇数,那么n变成n - 1的一半加一,直至n等于1为止。由于n每次都会除以2,所以上述做法的时间复杂度为O(logn)O(logn)

参考代码

class Main {
    public static int solution(int n) {
        // write code here
        int res = 0;
        while (n != 1) {
            if (n % 2 == 0) {
                res += n / 2;
                n /= 2;
            } else {
                res += (n - 1) / 2;
                n = (n - 1) / 2 + 1;
            }
        }
        return res;
    }

    public static void main(String[] args) {
        System.out.println(solution(7) == 6);
        System.out.println(solution(14) == 13);
        System.out.println(solution(1) == 0);
    }
}

44.小E的射击训练

问题描述

小E正在训练场进行射击练习,靶有10个环,靶心位于坐标(0, 0)。每个环对应不同的得分,靶心内(半径为1)得10分,依次向外的每个环分数减少1分。若射击点在某个半径为i的圆内,则得11-i分。如果射击点超出所有的环,则得0分。

根据给定的射击坐标(x, y),请计算小E的射击得分。

测试样例

样例1:

输入:x = 1, y = 0 输出:10

样例2:

输入:x = 1, y = 1 输出:9

样例3:

输入:x = 0, y = 5 输出:6

样例4:

输入:x = 3, y = 4 输出:6

解决方案

首先计算射击坐标和靶心的距离,并上取整,这个就表示落在哪一环内,然后用11减去它即可。注意:有可能距离过大,这个时候得分为0。上述做法的时间复杂度为O(1)O(1)

参考代码

public class Main {
    public static int solution(int x, int y) {
        // write code here
        int d = (int)Math.ceil(Math.sqrt(x * x + y * y));
        return Math.max(11 - d, 0);
    }

    public static void main(String[] args) {
        System.out.println(solution(1, 0) == 10);
        System.out.println(solution(1, 1) == 9);
        System.out.println(solution(0, 5) == 6);
        System.out.println(solution(3, 4) == 6);
    }
}

52.简单四则运算解析器

问题描述

小F面临一个编程挑战:实现一个基本的计算器来计算简单的字符串表达式的值。该字符串表达式有效,并可能包含数字(0-9)、运算符+-及括号()。注意,字符串中不包含空格。除法运算应只保留整数结果。请实现一个解析器计算这些表达式的值,且不使用任何内置的eval函数。

测试样例

样例1:

输入:expression = "1+1" 输出:2

样例2:

输入:expression = "3+4*5/(3+2)" 输出:7

样例3:

输入:expression = "4+2*5-2/1" 输出:12

样例4:

输入:expression = "(1+(4+5+2)-3)+(6+8)" 输出:23

样例5:

输入:expression = "2*(5+5*2)/3+(6+8*3)" 输出:40

解决方案

这个问题其实就是中缀表达式求值问题。首先,开两个栈,分别用来存储操作数和操作符,开一个哈希表用来存储操作符的优先级,乘号和除号的优先级高于加号和减号。然后,遍历字符串,如果当前字符是数字,加入操作数栈中;如果当前字符是'(',加入操作符栈中;如果当前字符是')',则只要操作符栈的栈顶不为'(',就一直进行运算,然后把栈顶的'('出栈;如果当前字符是操作符,那么只要操作符栈的栈顶元素不为'('并且栈顶元素的优先级高于当前字符,就一直进行运算,然后把当前字符入栈。当字符串遍历结束的时候,只要操作符栈不为空,就一直进行运算。最后操作数栈的栈顶元素就是结果。由于需要多次进行运算操作,可以把它单独写成一个函数,具体就是每次取出操作符栈的栈顶元素和取两次操作数栈的栈顶元素(因为是二元运算符),然后分加减乘除四种情况进行运算,最后把结果加入到操作数栈中。

参考代码

import java.util.*;
    public class Main {
        static Stack<Integer> nums = new Stack<>();
        static Stack<Character> ops = new Stack<>();
        static HashMap<Character, Integer> pr = new HashMap<>();
    public static int solution(String expression) {
        // Please write your code here
        pr.put('+', 1);
        pr.put('-', 1);
        pr.put('/', 2);
        pr.put('*', 2);
        for (int i = 0; i < expression.length(); i++) {
            char c = expression.charAt(i);
            if (c >= '0' && c <= '9') {
                nums.push(c - '0');
            } else if (c == '(') {
                ops.push(c);
            } else if (c == ')') {
                while (ops.peek() != '(') {
                    eval();
                }
                ops.pop();
            } else {
                while (ops.size() > 0 && ops.peek() != '(' && pr.get(ops.peek()) >= pr.get(c)) {
                    eval();
                }
                ops.push(c);
            }
        }
        while (ops.size() > 0) {
            eval();
        }
        return nums.peek();
    }

    public static void eval() {
        int b = nums.peek();
        nums.pop();
        int a = nums.peek();
        nums.pop();
        char c = ops.peek();
        ops.pop();
        int t;
        if (c == '+') {
            t = a + b;
        } else if (c == '-') {
            t = a - b;
        } else if (c == '*') {
            t = a * b;
        } else {
            t = a / b;
        }
        nums.push(t);
    }

    public static void main(String[] args) {
        // You can add more test cases here
        System.out.println(solution("1+1") == 2);
        System.out.println(solution("3+4*5/(3+2)") == 7);
        System.out.println(solution("4+2*5-2/1") == 12);
        System.out.println(solution("(1+(4+5+2)-3)+(6+8)") == 23);
    }
}

78.分组飞行棋棋子

问题描述

小M和小F在玩飞行棋。游戏结束后,他们需要将桌上的飞行棋棋子分组整理好。现在有 N 个棋子,每个棋子上有一个数字序号。小M的目标是将这些棋子分成 M 组,每组恰好5个,并且组内棋子的序号相同。小M希望知道是否可以按照这种方式对棋子进行分组。

例如,假设棋子序号为 [1, 2, 3, 4, 5],虽然只有5个棋子,但由于序号不同,因此不能形成有效的分组。如果序号是 [1, 1, 1, 1, 1, 2, 2, 2, 2, 2],则可以形成两个有效分组,因此输出为 True

测试样例

样例1:

输入:nums = [1, 2, 3, 4, 5] 输出:"False"

样例2:

输入:nums = [1, 1, 1, 1, 2, 1, 2, 2, 2, 2] 输出:"True"

样例3:

输入:nums = [5, 5, 5, 5, 5, 5, 5, 5, 5, 5] 输出:"True"

样例4:

输入:nums = [7, 7, 7, 8, 8, 8, 8, 8, 7, 7] 输出:"True"

样例5:

输入:nums = [9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9] 输出:"False"

解决方案

遍历该数字序列,用哈希表记录每个数的出现次数,然后看这些出现次数是否都是5的倍数,如果是,则可以分组成功,否则不能。上述做法的时间复杂度为O(n)O(n)

参考代码

import java.util.HashMap;

public class Main {
    public static String solution(int[] nums) {
        // Please write your code here
        HashMap<Integer, Integer> mp = new HashMap<>();
        for (int i = 0; i < nums.length; i++) {
            mp.put(nums[i], mp.getOrDefault(nums[i], 0) + 1);
        }
        for (Integer x : mp.keySet()) {
            if (mp.get(x) % 5 != 0) {
                return "False";
            }
        }
        return "True";
    }

    public static void main(String[] args) {
        // You can add more test cases here
        System.out.println(solution(new int[]{1, 3, 4, 5, 6, 5, 4}).equals("False"));
        System.out.println(solution(new int[]{1, 1, 1, 1, 2, 1, 2, 2, 2, 2}).equals("True"));
        System.out.println(solution(new int[]{11, 45, 49, 37, 45, 38, 3, 47, 35, 49, 26, 16, 24, 4, 45, 39, 28, 26, 14, 22, 4, 49, 18, 4, 4, 26, 47, 14, 1, 21, 9, 26, 17, 12, 44, 28, 24, 24, 10, 31, 33, 32, 23, 41, 41, 19, 17, 24, 28, 46, 28, 4, 18, 23, 48, 45, 7, 21, 12, 40, 2, 19, 19, 28, 32, 6, 27, 43, 6, 18, 8, 27, 9, 6, 6, 31, 37, 15, 26, 20, 43, 3, 14, 40, 20}).equals("False"));
    }
}

100.统计班级中的说谎者

问题描述

在小C的班级里,有 N 个学生,每个学生的成绩是 A_i。小C发现了一件有趣的事:当且仅当某个学生的成绩小于或等于自己的有更多人时,这个学生会说谎。换句话说,如果分数小于等于他的学生数量大于比他分数高的学生数量,则他会说谎。

现在,小C想知道班里有多少个学生会说谎。

测试样例

样例1:

输入:A = [100, 100, 100] 输出:3

样例2:

输入:A = [2, 1, 3] 输出:2

样例3:

输入:A = [30, 1, 30, 30] 输出:3

样例4:

输入:A = [19, 27, 73, 55, 88] 输出:3

样例5:

输入:A = [19, 27, 73, 55, 88, 88, 2, 17, 22] 输出:5

解决方案

遍历该数组,对于每一个元素,统计有多少个元素比它大,有多少个元素小于等于它,如果小于等于它的元素的数量大于比它大的元素数量,那么答案加一。上述做法的时间复杂度为O(n2)O(n^2)

参考代码

public class Main {
    public static int solution(int[] A) {
        // Edit your code here
        int res = 0;
        for (int x : A) {
            int num1 = 0, num2 = 0;
            for (int y : A) {
                if (y <= x) {
                    num1++;
                } else {
                    num2++;
                }
            }
            if (num1 > num2) {
                res++;
            }
        }
        return res;
    }

    public static void main(String[] args) {
        // Add your test cases here
        System.out.println(solution(new int[]{100, 100, 100}) == 3);
        System.out.println(solution(new int[]{2, 1, 3}) == 2);
        System.out.println(solution(new int[]{30, 1, 30, 30}) == 3);
        System.out.println(solution(new int[]{19, 27, 73, 55, 88}) == 3);
        System.out.println(solution(new int[]{19, 27, 73, 55, 88, 88, 2, 17, 22}) == 5);
    }
}