牛客网算法刷题笔记:哈希表、贪心与动态规划实战

第一题:乒乓球筐(哈希表/计数)

📝 题目描述

Nowcoder有两盒(A、B)乒乓球,有红双喜的、有亚力亚的......现在他需要判别A盒是否包含了B盒中所有的种类,并且每种球的数量不少于B盒中的数量。

输入描述:

输入有多组数据。每组数据包含两个字符串A、B,代表A盒与B盒中的乒乓球,每个乒乓球用一个大写字母表示,即相同类型的乒乓球为相同的大写字母。字符串长度不大于10000。

输出描述:

每一组输入对应一行输出:如果B盒中所有球的类型在A中都有,并且每种球的数量都不大于A,则输出"Yes";否则输出"No"。

💡 解题思路

这是一道典型的哈希表(计数数组) 应用题。

因为题目明确说明乒乓球种类由大写字母表示(A-Z),所以我们可以用一个长度为26的整型数组充当哈希表。

  1. 遍历字符串A,统计每种大写字母出现的次数。

  2. 遍历字符串B,每遇到一个字符,就将哈希表中对应字母的计数减1。

  3. 如果在减的过程中,某个字母的计数小于0,说明A盒中该类型的球数量少于B盒,直接输出"No"并跳出。

  4. 如果遍历完B都没有出现负数,则输出"Yes"。

💻 JAVA代码实现

java 复制代码
import java.util.Scanner;
public class Main {
    public static void main(String[] args) {
        Scanner in = new Scanner(System.in);
        while(in.hasNext()) {
            char[] s1 = in.next().toCharArray();
            char[] s2 = in.next().toCharArray();

            int[] hash = new int[26];

            // 统计A盒中每种球的个数
            for(int i = 0; i < s1.length; i++) {
                hash[s1[i] - 'A']++;
            }

            boolean ans = true;
            // 遍历B盒,消耗A盒的数量
            for(int i = 0; i < s2.length; i++) {
                hash[s2[i] - 'A']--;
                if(hash[s2[i] - 'A'] < 0) {
                    System.out.println("No");
                    ans = false;
                    break;
                }
            }
            if(ans) System.out.println("Yes");
        }
    }
}

第二题:组队竞赛(排序 + 贪心)

📝 题目描述

牛牛举办了一次编程比赛,参加比赛的有 3∗n3∗n 个选手,每个选手都有一个水平值 aiai​。现在要将这些选手进行组队,一共组成n个队伍,即每个队伍3人。牛牛发现队伍的水平值等于该队伍队员中第二高水平值。

为了让比赛更有看点,牛牛想安排队伍使所有队伍的水平值总和最大。

输入描述:

输入的第一行为一个正整数n (1≤n≤1051≤n≤105)。

第二行包括 3∗n3∗n 个整数 aiai​ (1≤ai≤1091≤ai​≤109),表示每个参赛选手的水平值。

输出描述:

输出一个整数表示所有队伍的水平值总和最大值。

💡 解题思路

这是一道经典的贪心算法 题。

为了使所有队伍的"第二高水平值"之和最大,我们需要合理分配每组的三个人。

假设我们将数组从小到大排序。对于每个队伍,如果要让第二大的值尽可能大,我们应该:

  • 选一个最小的值作为队伍的"拖油瓶"(第一水平)。

  • 选一个最大的值作为队伍的"天花板"(第三水平)。

  • 选一个尽可能大的值作为"第二水平"。

具体策略:

  1. 将数组升序排序。

  2. 从数组倒数第二个元素开始(即最大的值不能被选为第二水平,它只能作为每组的最大值被牺牲掉),每次向前隔一个取一个值,一共取n个。

  3. 例如:排序后 [1, 2, 5, 5, 5, 8]。分组为 [1, 5, 8] 和 [2, 5, 5]。第二水平值分别是5和5,总和为10。

  4. 代码实现上,排序后,定义指针 pos = 3*n - 2,每次 pos -= 2,累加n次即可。注意结果需要用 long 防止溢出。

💻 JAVA代码实现

java 复制代码
import java.util.*;
public class Main {
    public static void main(String[] args) {
        Scanner in = new Scanner(System.in);
        int n = in.nextInt();
        int[] arr = new int[n * 3];
        for(int i = 0; i < n * 3; i++) {
            arr[i] = in.nextInt();
        }
        Arrays.sort(arr);
        
        int pos = n * 3 - 2, count = 1;
        long ret = 0;
        // 贪心累加每组的第二水平值
        while(count++ <= n) {
            ret += arr[pos];
            pos -= 2;
        }
        System.out.println(ret);
    }
}

第三题:删除相邻数字的最大分数(动态规划)

📝 题目描述

给定一个长度为n的仅包含正整数的数组,另外有一些操作,每次操作你可以选择数组中的任意一个元素 aiai​,同时数组中所有等于 ai−1ai​−1 和 ai+1ai​+1 的元素会被全部移除,同时你可以得到 aiai​ 分,直到所有的元素都被选择或者删除。

请你计算最多能得到多少分。

数据范围:数组长度满足 1≤n≤1051≤n≤105,数组中的元素大小都满足 1≤ai≤1041≤ai​≤104。

💡 解题思路

这道题是经典的动态规划 问题,类似于力扣上的"打家劫舍"或者"删除并获得点数"。

虽然题目描述是"删除相邻数字",但我们可以转换思路:

  1. 由于元素值域在 1∼1041∼104,我们可以开一个数组 sum[],sum[x] 表示原数组中所有值为 x 的元素之和。

  2. 这样问题就转化为:在 1 ~ 10000 这些数字中,如果选择了数字 i,就能获得 sum[i] 的分数,但数字 i-1 和 i+1 就不能选了。

  3. 定义状态:

    • f[i] 表示选择数字 i 时能获得的最大分数。

    • g[i] 表示不选择数字 i 时能获得的最大分数。

  4. 状态转移方程:

    • 如果选了 i,那么 i-1 一定不能选,所以 f[i] = g[i-1] + sum[i]。

    • 如果不选 i,那么 i-1 选或不选都可以,取最大值,所以 g[i] = Math.max(f[i-1], g[i-1])。

  5. 最终答案就是 Math.max(f[N-1], g[N-1])。

💻 JAVA代码实现

java 复制代码
import java.util.*;

public class Main {
    static int N = (int)1e4 + 10;
    static int[] sum = new int[N]; // 记录每个数字的总和
    static int[] f = new int[N];   // 选当前数字的最大分数
    static int[] g = new int[N];   // 不选当前数字的最大分数

    public static void main(String[] args) {
        Scanner in = new Scanner(System.in);
        int n = in.nextInt();
        int x = 0;
        // 预处理:将相同数字的分数合并
        for(int i = 0; i < n; i++) {
            x = in.nextInt();
            sum[x] += x;
        }
        
        // 动态规划过程
        for(int i = 1; i < N; i++) {
            f[i] = g[i - 1] + sum[i];
            g[i] = Math.max(f[i - 1], g[i - 1]);
        }
        
        System.out.println(Math.max(f[N - 1], g[N - 1]));
    }
}
相关推荐
careathers1 小时前
【数据结构】二叉树
数据结构·算法
李游Leo2 小时前
HarmonyOS 7 + ArkTS + NAPI 学习笔记:Native 模块桥接、CMake 构建与跨语言调用实践【鸿蒙心迹】
笔记·学习·harmonyos
Omics Pro2 小时前
空间组学!医学代谢显微工具
数据库·人工智能·算法·机器学习·自然语言处理
Coder-magician2 小时前
《代码随想录》刷题打卡day44:图论-part02
算法·深度优先·图论
克里斯蒂亚诺更新2 小时前
理解机器学习的各种算法
人工智能·算法·机器学习
小范的技术工坊2 小时前
大模型学习操作文档(13个核心概念串讲)
人工智能·学习·算法·大模型
索端阳2 小时前
I.MX6ULL 裸机开发:LCD Framebuffer 与 RGB888 学习笔记
笔记·vscode·嵌入式硬件·学习
一个低调的青年3 小时前
电池健康状态估计(一)
经验分享·笔记·其他·算法·模型
longlongzihan3 小时前
【LeetCode 204. 计数质数】从暴力枚举到埃拉托斯特尼筛法
算法·筛法