【笔试刷题】小米-2026.03.14-第二套-改编真题

✅ 春招备战指南 ✅

💡 学习建议:

  • 先尝试独立解题
  • 对照解析查漏补缺

🧸 题面描述背景等均已深度改编,做法和题目本质基本保持一致。

🍹 感谢各位朋友们的订阅,你们的支持是我们创作的最大动力

🌸 目前本专栏已经上线200+套真题改编解析,后续会持续更新的

春秋招笔试机考招合集 -> 互联网必备刷题宝典🔗

小米-2026.03.14-第二套

小米-2026.03.14-第二套

这套题的梯度很顺。第一题是实现型热身,关键在于把比较规则一层不漏地写对;第二题才是真正的分水岭,必须先把错误题意修正成和样例一致的版本,再去做区间 DP;第三题则是经典贪心模型,能不能一眼认出“截止日排序 + 最小堆”基本决定了写题速度。

题目一:定制选品单

题面看起来只是重新排商品顺序,但真正的排序键不是固定的,而是顾客临时给出的一组维度优先级。因为 很小,直接按顾客顺序做多关键字比较就够了,别把矩阵读反就行。

难度:简单

题目二:并炉品阶

这题最麻烦的不是实现,而是原始题面规则和样例互相矛盾。正式版必须按样例修正成“若 则合成后是 ,否则是 ”,之后用区间 DP 维护每一段能炼出的所有品阶。

难度:中等

题目三:奖章作业表

本质是单位时长任务选择问题。把作业按截止日排序,堆里始终只保留当前能按时做完的最高收益集合;一旦数量超过当前截止日,就删掉收益最小的那份作业。

难度:中等

定制选品单

问题描述

小基 正在为一位顾客整理门店首页的推荐顺序。

店里共有 件商品,每件商品都有 个评价维度。顾客不会直接给出一个总分,而是会给出一个长度为 的排列 ,表示他看重这些维度的先后顺序。

比较两件商品时,规则如下:

  • 先比较它们在维度 上的分数,分数更高的排在前面。
  • 如果这一维相同,就继续比较维度
  • 按同样方式一路比较到维度

已知任意两件商品在全部 个维度上的分数不会完全一样。请输出按顾客偏好从高到低排好序后的商品编号。

输入格式

第一行输入两个整数 ,表示商品数量和评价维度数量。

第二行输入 个整数 ,它们是 的一个排列,表示顾客给出的维度优先级。

接下来输入一个 的分数矩阵。

  • 行第 列的整数 ,表示商品 在第 个维度上的分数。

输出格式

输出一行 个整数,表示按顾客偏好从高到低排序后的商品编号。

样例输入

5 3
1 3 2
4 4 3 4 3
5 2 4 4 4
3 5 2 3 3

样例输出

2 1 4 5 3

样例说明

样例 解释说明
样例 1 顾客先看第 维,再看第 维,最后看第 维。商品 和商品 在第一维同分,但商品 的第三维更高,所以排在最前。商品 与商品 的前两层比较仍然相同,最后由第二维分出先后,因此最终顺序是 2 1 4 5 3

数据范围

  • 的一个排列
  • 保证不存在两件商品在全部维度上的分数完全相同

题解

这题没有隐藏做法,本质就是把“顾客给出的维度顺序”变成真正的排序键。

每件商品都对应一个长度为 的分数向量,但比较时不能按维度编号 直接看,而是必须严格按照顾客给出的顺序 去逐项比较。

因为 ,一次比较最多只会看 个维度,所以直接排序就足够了:

  1. 先读入顾客给出的维度顺序。
  2. 再读入分数矩阵,保留“第几维、哪件商品”的分数。
  3. 排序时枚举顾客给出的维度顺序,找到第一处不同分数,分高的商品排前面。

题目保证不存在两个商品在全部维度上完全相同,所以比较过程一定能在某一维分出先后。

时间复杂度是 。这里的 很小,可以直接看成常数;空间复杂度是 ,用于存储分数矩阵。

参考代码

  • Python
import sys

input = lambda: sys.stdin.readline().strip()


def solve() -> None:
    n, k = map(int, input().split())
    ords = [int(x) - 1 for x in input().split()]
    sc = [list(map(int, input().split())) for _ in range(k)]

    ids = list(range(n))

    def key(i: int):
        # 直接把顾客关心的分数顺序拼成排序键。
        return tuple(-sc[d][i] for d in ords)

    ids.sort(key=key)
    print(*[i + 1 for i in ids])


if __name__ == "__main__":
    solve()
  • Cpp
#include <bits/stdc++.h>
using namespace std;

int main() {
    ios::sync_with_stdio(false);
    cin.tie(nullptr);

    int n, k;
    cin >> n >> k;

    vector<int> ord(k);
    for (int i = 0; i < k; ++i) {
        cin >> ord[i];
        --ord[i];
    }

    vector<vector<int>> sc(k, vector<int>(n));
    for (int i = 0; i < k; ++i) {
        for (int j = 0; j < n; ++j) {
            cin >> sc[i][j];
        }
    }

    vector<int> ids(n);
    iota(ids.begin(), ids.end(), 0);

    sort(ids.begin(), ids.end(), [&](int x, int y) {
        // 按顾客给出的维度顺序依次比较。
        for (int d : ord) {
            if (sc[d][x] != sc[d][y]) {
                return sc[d][x] > sc[d][y];
            }
        }
        return x < y;
    });

    for (int i = 0; i < n; ++i) {
        if (i) {
            cout << ' ';
        }
        cout << ids[i] + 1;
    }
    cout << '\n';
    return 0;
}
  • Java
import java.io.BufferedInputStream;
import java.io.IOException;
import java.util.Arrays;

public class Main {
    static class FastScanner {
        private final BufferedInputStream in = new BufferedInputStream(System.in);
        private final byte[] buf = new byte[1 << 16];
        private int len = 0;
        private int ptr = 0;

        private int read() throws IOException {
            if (ptr >= len) {
                len = in.read(buf);
                ptr = 0;
                if (len <= 0) {
                    return -1;
                }
            }
            return buf[ptr++];
        }

        int nextInt() throws IOException {
            int c;
            do {
                c = read();
            } while (c <= 32 && c != -1);

            int sign = 1;
            if (c == '-') {
                sign = -1;
                c = read();
            }

            int val = 0;
            while (c > 32) {
                val = val * 10 + c - '0';
                c = read();
            }
            return val * sign;
        }
    }

    public static void main(String[] args) throws Exception {
        FastScanner fs = new FastScanner();
        int n = fs.nextInt();
        int k = fs.nextInt();

        int[] ord = new int[k];
        for (int i = 0; i < k; i++) {
            ord[i] = fs.nextInt() - 1;
        }

        int[][] sc = new int[k][n];
        for (int i = 0; i < k; i++) {
            for (int j = 0; j < n; j++) {
                sc[i][j] = fs.nextInt();
            }
        }

        Integer[] ids = new Integer[n];
        for (int i = 0; i < n; i++) {
            ids[i] = i;
        }

        Arrays.sort(ids, (x, y) -> {
            // 按顾客给出的维度优先级逐项比较。
            for (int d : ord) {
                if (sc[d][x] != sc[d][y]) {
                    return Integer.compare(sc[d][y], sc[d][x]);
                }
            }
            return Integer.compare(x, y);
        });

        StringBuilder out = new StringBuilder();
        for (int i = 0; i < n; i++) {
            if (i > 0) {
                out.append(' ');
            }
            out.append(ids[i] + 1);
        }
        out.append('\n');
        System.out.print(out);
    }
}

并炉品阶

问题描述

小柯正在整理一排待入炉的药胚。第 枚药胚的当前品阶是

每次操作只能选择两个相邻药胚合并。设它们当前品阶分别为 ,那么新药胚的品阶按下面的正式规则计算:

  • 如果 ,合并结果是
  • 否则,合并结果是

合并后,新药胚会留在原来的位置,整排药胚长度减一。不断操作,直到整排只剩下一枚药胚为止。

请计算:

  1. 最终药胚可能达到的最高品阶。
  2. 最终药胚可能出现的不同品阶数量。

输入格式

第一行输入两个正整数

第二行输入 个正整数 ,表示初始药胚的品阶。

输出格式

输出一行两个整数,分别表示:

  • 最终药胚可能达到的最高品阶。
  • 最终药胚可能出现的不同品阶数量。

样例输入

6 2
2 1 5 1 1 2

样例输出

7 3

样例说明

样例 解释说明
样例 1 合并顺序不同,最终品阶会不同。按某种顺序可以做到最高品阶 ;把所有合法合并顺序考虑完,最终一共会出现 种不同品阶,所以输出 7 3

数据范围

题解

这题最重要的一步,是把正式规则固定成题面现在这版:

  • ,结果是
  • 否则结果是

一旦规则确定,问题就变成了“这一段药胚最后能炼出哪些品阶”。

can[l][r] 表示区间 最终可能得到的所有品阶集合。

1. 状态定义

当区间里只有一枚药胚时,答案只有它自己:

can[i][i] = {a_i}

2. 为什么可以做区间 DP

区间 的最后一次合并,一定是把它拆成两段:

  • 左边是
  • 右边是

左段先独立合成一枚药胚,右段也独立合成一枚药胚,最后再把这两枚合在一起。

所以只要枚举最后一次合并的位置 ,再把 can[l][m]can[m+1][r] 里的所有可能值按规则合并,就能得到 can[l][r]

3. 朴素转移为什么还不够

如果直接枚举左值 、右值 ,一次转移会是 ,其中 是可能的品阶范围。

这里初始品阶不超过 ,每次合并最多加 ,所以最终品阶不会超过 。也就是说

虽然范围不大,但总共有很多个区间和拆分点,直接四重枚举还是显得笨。

4. 把一次集合合并降到

固定较大的那个值为

如果想让结果还是 ,说明较小值必须满足:

因为这时差值严格大于 ,结果不会升阶。

如果想让结果变成 ,说明较小值必须落在:

因为这时差值不超过 ,会升一阶。

于是对左右两个集合各做一遍前缀计数,就能在 时间判断:

  • 另一边是否存在一个值落在
  • 另一边是否存在一个值落在

这样一次“两个集合的合并”就能压到

5. 复杂度

区间 DP 一共有 个“区间 + 拆分点”组合。

每次集合合并是 ,而

所以总时间复杂度是 ,在这题的数据范围里完全可行;空间复杂度是

参考代码

  • Python
import sys

input = lambda: sys.stdin.readline().strip()


def upd(dst, left, right, k, mx):
    # 前缀计数用于判断“某个范围内是否存在可行值”。
    pl = [0] * (mx + 1)
    pr = [0] * (mx + 1)
    for v in range(1, mx + 1):
        pl[v] = pl[v - 1] + left[v]
        pr[v] = pr[v - 1] + right[v]

    for v in range(1, mx + 1):
        if left[v]:
            lo = max(1, v - k)
            # 若右边有值落在 [v-k, v],则能升到 v+1。
            if v + 1 <= mx and pr[v] - pr[lo - 1] > 0:
                dst[v + 1] = 1
            # 若右边有值 <= v-k-1,则结果保持在 v。
            hi = v - k - 1
            if hi >= 1 and pr[hi] > 0:
                dst[v] = 1

        if right[v]:
           

剩余60%内容,订阅专栏后可继续查看/也可单篇购买

互联网刷题笔试宝典 文章被收录于专栏

互联网刷题笔试宝典,这里涵盖了市面上大部分的笔试题合集,希望助大家春秋招一臂之力

全部评论

相关推荐

评论
1
1
分享

创作者周榜

更多
牛客网
牛客网在线编程
牛客网题解
牛客企业服务