【笔试刷题】阿里系列-2026.03.25-研发岗-改编真题

✅ 春招备战指南 ✅

💡 学习建议:

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

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

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

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

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

阿里系列-2026.03.25-研发岗

1. 小兰的仓位配货表

问题描述

说明:阿里系列近期多条业务线笔试题基本共用同一套公开机试,淘天、阿里云等方向都可参考本场。

小兰正在整理一份仓位配货表。现在有 类货物,第 类共有 件,需要分配给 个仓位。分配时需要满足:

  • 每一类货物都必须分配完;
  • 对于同一类货物,任意两个仓位拿到的数量之差不超过

在所有合法分配方案中,任选一个仓位,统计这个仓位最终拿到的货物总数。请输出该总数可能达到的最小值和最大值。

输入格式

每个测试文件包含多组数据。

  • 第一行输入整数 ),表示数据组数。
  • 对于每组数据:
  • 第一行输入两个整数 );
  • 第二行输入 个整数 )。

保证单个测试文件内所有数据组的 之和不超过

输出格式

对于每组数据输出一行两个整数,分别表示单个仓位最终货物总数的最小值与最大值。

样例输入

3
3 2
5 2 7
4 3
0 0 0 0
2 5
10 1

样例输出

6 8
0 0
2 3

数据范围

题解

这题最容易想偏的地方,是把所有货物揉成一个总量去看。
题目的约束落在“每一种货物都要单独满足差值不超过 ”这一层。只要把这个限制拆开,整题就只剩下简单加法。

先只看某一类货物

把它分给 个仓位时,所有仓位的数量差不能超过 ,因此分法已经基本被固定了:

  • 每个仓位至少拿到 件;
  • ,说明还有若干件“余量”,它们只能分散到若干个仓位里,并且每个仓位至多再多拿 件。

于是对某一个固定仓位来说,这一类货物的贡献只有两种:

  • 拿到基础份额
  • 或者在基础份额上再多拿

接下来把所有类别的贡献相加。

最小值怎么来

如果想让这个固定仓位拿到尽量少,那么每一类都只给它基础份额即可,因此最小值就是

最大值怎么来

如果某一类满足 ,就存在一个仓位可以在基础份额上再多拿
而不同类别之间互不影响,所以这些“额外的 ”完全可以都叠到同一个仓位身上。

因此最大值就是

这也解释了为什么不需要更复杂的贪心或 DP:
每一类货物的选择都是独立的,最后只是在做求和。

实现时线性扫描一次数组:

  • mn 累加所有基础份额;
  • extra 统计有多少类存在余数。

单组时间复杂度是 ,总复杂度是 ,空间复杂度

参考代码

  • Python
import sys


def solve() -> None:
    input = sys.stdin.readline
    t = int(input().strip())
    out = []
    for _ in range(t):
        # n 是货物种类数,k 是仓位数。
        n, k = map(int, input().split())
        arr = list(map(int, input().split()))
        # mn: 固定仓位至少能拿到的总件数。
        mn = 0
        # extra: 有多少类货物还能让这个仓位再多拿 1 件。
        extra = 0
        for c in arr:
            # 基础份额一定会落到每个仓位上。
            mn += c // k
            # 有余数时,说明某些仓位还能额外多拿 1 件。
            if c % k != 0:
                extra += 1
        # 同一个仓位最多从每个“有余数的类别”里再多拿 1 件。
        out.append(f"{mn} {mn + extra}")
    # 按题目要求逐组输出答案。
    sys.stdout.write("\n".join(out))


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

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

    int T;
    // T 组询问,逐组独立计算。
    cin >> T;
    while (T--) {
        int n;
        int64 k;
        cin >> n >> k;

        // mn: 单个仓位的保底总数。
        int64 mn = 0;
        // extra: 可以再额外 +1 的类别个数。
        int64 extra = 0;
        for (int i = 0; i < n; ++i) {
            int64 c;
            cin >> c;
            // c / k 是这一类货物对任意仓位的基础贡献。
            mn += c / k;
            // 只要有余数,就存在仓位能再多拿 1 件。
            if (c % k != 0) {
                ++extra;
            }
        }

        // 最大值 = 基础份额总和 + 所有可额外贡献的类别数。
        cout << mn << ' ' << mn + extra << '\n';
    }
    return 0;
}
  • Java
import java.io.*;
import java.util.*;

public class Main {
    static class FastScanner {
        private final InputStream in = System.in;
        private final byte[] buffer = new byte[1 << 16];
        private int ptr = 0, len = 0;

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

        long nextLong() throws IOException {
            int c;
            do {
                c = read();
            } while (c <= ' ' && c != -1);
            long sign = 1;
            if (c == '-') {
                sign = -1;
                c = read();
            }
            long val = 0;
            while (c > ' ') {
                val = val * 10 + (c - '0');
                c = read();
            }
            return val * sign;
        }
    }

    public static void main(String[] args) throws Exception {
        FastScanner fs = new FastScanner();
        StringBuilder sb = new StringBuilder();
        int t = (int) fs.nextLong();
        while (t-- > 0) {
            int n = (int) fs.nextLong();
            long k = fs.nextLong();
            // mn 表示固定仓位至少能分到多少件。
            long mn = 0;
            // extra 表示还能多拿 1 件的货物类别数。
            long extra = 0;
            for (int i = 0; i < n; i++) {
                long c = fs.nextLong();
                // 这一类货物平均分下来的基础份额。
                mn += c / k;
                // 有余数,说明某个仓位可以再多拿 1。
                if (c % k != 0) extra++;
            }
            // 输出单个仓位可能达到的最小值和最大值。
            sb.append(mn).append(' ').append(mn + extra).append('\n');
        }
        // 统一输出,避免多次打印带来的额外开销。
        System.out.print(sb);
    }
}

2. 小柯的双榜对齐

问题描述

说明:阿里系列近期多条业务线笔试题基本共用同一套公开机试,淘天、阿里云等方向都可参考本场。

小柯手上有两份候选榜单,分别记为排列 ,长度都为 ,元素都是 且互不重复。她想从两份榜单里挑出一条公共子序列,并让这条子序列的字典序尽可能大。请你输出这条序列。

定义说明:

  • 字典序:从左到右比较第一个不同位置,数值更大的序列字典序更大;若一个是另一个的前缀,则更长者更大。
  • 子序列:从原序列删除若干元素(可为 个)后保持相对顺序得到的序列。

输入格式

  • 第一行输入整数 )。
  • 第二行输入 个整数 ,表示排列
  • 第三行输入 个整数 ,表示排列

保证 都是 的排列。

输出格式

  • 第一行输出一个整数 ,表示答案序列长度。
  • 第二行输出 个整数,表示字典序最大的公共子序列。

样例输入

5
2 5 3 1 4
5 2 4 3 1

样例输出

2
5 4

数据范围

  • 均为 的排列

题解

这题表面上像公共子序列,很多人会下意识往 DP 想。
但这里有个很关键的限制: 都是排列,每个值只出现一次。这个条件把问题简化得非常彻底。

先把“公共子序列”翻译成位置条件

设:

  • posP[x] 表示值 在排列 中的位置;
  • posQ[x] 表示值 在排列 中的位置。

如果当前答案最后一个数在两边的位置分别是 (curP, curQ),那么下一个值 能接进答案,当且仅当

因为排列里没有重复值,所以一个值要么能接,要么不能接,不会出现“同一个值在后面还有别的位置可选”的情况。

字典序最大意味着什么

字典序比较时,先看第一位;第一位相同,再看第二位;依次类推。
所以只要当前位置有更大的值能选,就绝对不能先拿更小的值。

这就把整题变成了一个非常直接的贪心:

  1. 预处理每个值在两个排列中的位置。
  2. 维护当前已经选到哪里,记为 (curP, curQ)
  3. 按值从大到小枚举 x = n, n-1, ..., 1
  4. x 在两个排列中的位置都还在当前末尾之后,就立刻把它加入答案。

为什么这个贪心成立

假设当前有两个可选值

  • 如果先选 ,那么答案在这一位就已经比“先选 ”更小了;
  • 后面无论补什么,都无法弥补第一个分歧位置吃掉的损失。

因此当前位置一定要优先拿最大的可行值。

而一旦选定了这个最大值,后面的要求仍然完全一样:
只是在两个排列中,把可选范围缩到了更靠后的后缀里。于是同样的贪心可以继续做下去。

为什么不用 DP

普通 LCS 之所以麻烦,是因为有重复值,同一个字符可以有很多种对齐方式。
本题里每个值只出现一次,状态压缩成了“当前位置之后还能不能接上”。所以只用一次从大到小的扫描就够了。

时间复杂度 ,空间复杂度

参考代码

  • Python
import sys


def solve() -> None:
    input = sys.stdin.readline
    n = int(input().strip())
    p = list(map(int, input().split()))
    q = list(map(int, input().split()))

    # pos_p[x] / pos_q[x] 记录值 x 在两份排列中的位置。
    pos_p = [0] * (n + 1)
    pos_q = [0] * (n + 1)
    for i, x in enumerate(p, 1):
        # 记录每个值在第一份排列中的位置。
        pos_p[x] = i
    for i, x in enumerate(q, 1):
        # 记录每个值在第二份排列中的位置。
        pos_q[x] = i

    # 当前答案最后一个值在两边对应的位置。
    cur_p = 0
    cur_q = 0
    # ans 按加入顺序存放最终答案。
    ans = []
    for x in range(n, 0, -1):
        # 只有当 x 在两边都还能接到当前答案末尾之后,才能加入公共子序列。
        if pos_p[x] > cur_p and pos_q[x] > cur_q:
            ans.append(x)
            cur_p = pos_p[x]
            cur_q = pos_q[x]

    # 题目要求先输出长度,再输出具体序列。
    print(len(ans))
    print(*ans)


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

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

    int n;
    cin >> n;
    // posP[x] / posQ[x] 记录值 x 在两份排列中的出现位置。
    vector<int> posP(n + 1), posQ(n + 1);

    for (int i = 1; i <= n; ++i) {
        int x;
        cin >> x;
        // 值 x 在 p 中只出现一次,直接记位置。
        posP[x] = i;
    }
    for (int i = 1; i <= n; ++i) {
        int x;
        cin >> x;
        // 值 x 在 q 中只出现一次。
        posQ[x] = i;
    }

    // 当前答案末尾在 p、q 中分别停在哪里。
    int curP = 0, curQ = 0;
    // ans 顺序保存最终公共子序列。
    vector<int> ans;
    ans.reserve(n);

    for (int x = n; x >= 1; --x) {
        // x 若能同时接在两边当前末尾之后,就应该优先选它。
        if (posP[x] > curP && posQ[x] > curQ) {
            ans.push_back(x);
            // 一旦选了 x,后续值必须同时出现在这两个位置之后。
            curP = posP[x];
            curQ = posQ[x];
        }
    }

    // 输出答案长度和具体序列。
    cout << ans.size() << '\n';
    for (int i = 0; i < (int)ans.size(); ++i) {
        if (i) cout << ' ';
        cout << ans[i];
    }
    cout << '\n';
    return 0;
}
  • Java
import java.io.*;
import java.util.*;

public class Main {
    static class FastScanner {
        private final InputStream in = System.in;
        private final byte[] buffer = new byte[1 << 16];
        private int ptr = 0, len = 0;

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

        int nextInt()

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

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

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

全部评论
第三题呢
点赞 回复 分享
发布于 今天 11:16 北京

相关推荐

对空六翼:你真幸运,碰见这么好的人,不像我,秋招的时候被室友骗进cx了
实习好累,可以辞职全力准...
点赞 评论 收藏
分享
评论
点赞
收藏
分享

创作者周榜

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