【笔试刷题】阿里系列-2026.04.08-算法岗-改编真题

✅ 春招备战指南 ✅

💡 学习建议:

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

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

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

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

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

阿里系列-2026.04.08-算法岗

本场为阿里系共享机试,淘天、阿里云等方向均可参考,共 3 道编程题。整套题的结构比较分明:第一题是直角判定,考察基础几何与距离处理;第二题是典型的结构化计数,需要先识别合法子序列对应的是哪一段连续 1;第三题虽然题意像重排贪心,但核心模型其实是最小权完美匹配。

题目一:小基 的网格直角信标

关键在于把 3 个亮格直接视作平面上的 3 个点。题目里说的是“单元格中心”,但整体平移不会改变两点距离,所以直接用网格坐标计算三条边的平方距离,再套勾股关系判断即可。

难度:简单

题目二:小兰的亮灯片段子序列

这题的突破口是先把原串里所有 1 的位置抽出来。题目要求被选中的 1 在子序列里连续出现,且原串中相邻被选 1 之间不能再夹着别的 1,所以合法方案一定对应原串中一段连续的 1 位置块。块内 0 不能选,块外 0 都能自由取舍,答案自然转成幂次计数。

难度:中等

题目三:小柯的排座余数代价

这题最容易误入“排序 + 贪心”方向,但真正稳定的建模方式是把每个数和每个位置之间的代价写成 a_j mod i,然后做二分图最小权完美匹配。因为 n 很小,直接上 Hungarian 算法就足够。

难度:中等偏高

1. 小基 的网格直角信标

问题描述

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

小基 正在检查一块 的测试网格。网格中的每个单元格要么关闭,要么点亮,且整张网格里恰好只有 个单元格被点亮。

她把这 个点亮单元格的中心当作平面上的 个点,并把它们顺次连起来。现在她想知道,这 个点构成的三角形是不是直角三角形。

如果是直角三角形,输出 YES;否则输出 NO

输入格式

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

  • 第一行输入一个整数 ,表示测试数据组数。
  • 对于每组测试数据:
    • 第一行输入一个整数 ,表示网格边长。
    • 接下来输入 行,每行是一个长度为 的 01 字符串。

保证每组数据中恰好有 个字符为 1

输出格式

对于每组测试数据,输出一行一个字符串。

  • 如果对应三角形是直角三角形,输出 YES
  • 否则输出 NO

样例输入

2
3
001
000
101
3
001
010
100

样例输出

YES
NO

数据范围

  • 单个测试文件中,所有测试数据的 之和不超过
样例 解释说明
样例 1 第 1 组 三个点分别在 ,对应两条直角边长度平方都是 ,因此答案是 YES
样例 1 第 2 组 三个点共线,不会形成直角三角形,因此答案是 NO

题解

先把三个点找出来

因为整张图里恰好只有 1,所以直接扫完整个网格,把这 个位置记下来即可。

虽然题目说的是“单元格中心”,但每个点都只是统一平移了 ,两两之间的距离不会变。所以直接把格子下标当作平面坐标来算就行。

用平方距离判断直角

设三点分别是 ,那么只需要算出:

把这三个值从小到大排序成

若三角形是直角三角形,那么一定满足勾股关系:

同时 不能为 ,否则说明有两个点重合,但这题里本身不会出现这种情况。

复杂度分析

每组数据只需要扫一遍网格,再做常数次计算。

  • 时间复杂度:
  • 空间复杂度:(不计输入存储,仅保存 个点)

参考代码

  • Python
import sys

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


def dist2(a, b):
    dx = a[0] - b[0]
    dy = a[1] - b[1]
    return dx * dx + dy * dy


def solve():
    t = int(input())
    out = []

    for _ in range(t):
        n = int(input())
        pts = []
        for i in range(n):
            row = input()
            for j, ch in enumerate(row):
                if ch == "1":
                    pts.append((i, j))

        d = [
            dist2(pts[0], pts[1]),
            dist2(pts[0], pts[2]),
            dist2(pts[1], pts[2]),
        ]
        d.sort()
        out.append("YES" if d[0] > 0 and d[0] + d[1] == d[2] else "NO")

    sys.stdout.write("\n".join(out))


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

static int dist2(const pair<int, int>& a, const pair<int, int>& b) {
    int dx = a.first - b.first;
    int dy = a.second - b.second;
    return dx * dx + dy * dy;
}

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

    int T;
    cin >> T;
    while (T--) {
        int n;
        cin >> n;

        vector<pair<int, int>> pts;
        for (int i = 0; i < n; ++i) {
            string s;
            cin >> s;
            for (int j = 0; j < n; ++j) {
                if (s[j] == '1') {
                    pts.push_back({i, j});
                }
            }
        }

        vector<int> d = {
            dist2(pts[0], pts[1]),
            dist2(pts[0], pts[2]),
            dist2(pts[1], pts[2]),
        };
        sort(d.begin(), d.end());

        cout << (d[0] > 0 && d[0] + d[1] == d[2] ? "YES" : "NO") << '\n';
    }

    return 0;
}
  • Java
import java.io.BufferedInputStream;
import java.io.IOException;

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

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

        String next() throws IOException {
            int c;
            do {
                c = read();
            } while (c <= ' ' && c != -1);

            StringBuilder sb = new StringBuilder();
            while (c > ' ') {
                sb.append((char) c);
                c = read();
            }
            return sb.toString();
        }

        int nextInt() throws IOException {
            return Integer.parseInt(next());
        }
    }

    static int dist2(int x1, int y1, int x2, int y2) {
        int dx = x1 - x2;
        int dy = y1 - y2;
        return dx * dx + dy * dy;
    }

    public static void main(String[] args) throws Exception {
        FastScanner fs = new FastScanner();
        StringBuilder out = new StringBuilder();

        int T = fs.nextInt();
        while (T-- > 0) {
            int n = fs.nextInt();
            int[] xs = new int[3];
            int[] ys = new int[3];
            int cnt = 0;

            for (int i = 0; i < n; ++i) {
                String s = fs.next();
                for (int j = 0; j < n; ++j) {
                    if (s.charAt(j) == '1') {
                        xs[cnt] = i;
                        ys[cnt] = j;
                        cnt++;
                    }
                }
            }

            int[] d = new int[3];
            d[0] = dist2(xs[0], ys[0], xs[1], ys[1]);
            d[1] = dist2(xs[0], ys[0], xs[2], ys[2]);
            d[2] = dist2(xs[1], ys[1], xs[2], ys[2]);

            java.util.Arrays.sort(d);
            out.append(d[0] > 0 && d[0] + d[1] == d[2] ? "YES" : "NO").append('\n');
        }

        System.out.print(out);
    }
}

2. 小兰的亮灯片段子序列

问题描述

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

小兰 手里有一个长度为 的 01 串 。她想从中删去若干字符,保留相对顺序不变,得到一个新的子序列。

她只关心满足下面三个条件的子序列数量:

  • 子序列里恰好有 1
  • 1 在子序列中必须连续出现;
  • 对于子序列中任意相邻的两个 1,它们在原串里之间不能夹着其他 1

请你计算满足要求的子序列个数。答案对 取模。

这里的“子序列”指的是:从原串中删除任意个字符(可以一个都不删),剩余字符保持相对顺序不变后得到的新串。

输入格式

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

  • 第一行输入一个整数 ,表示测试数据组数。
  • 对于每组测试数据:
    • 第一行输入两个整数
    • 第二行输入一个长度为 的 01 字符串

输出格式

对于每组测试数据,输出一行一个整数,表示满足条件的子序列数量。

样例输入

3
5 2
01101
4 1
0010
6 3
101101

样例输出

6
8
4

数据范围

  • 仅由 01 组成
  • 单个测试文件中,所有测试数据的 之和不超过
样例 解释说明
样例 1 第 1 组 取连续的两颗 1 有两种方式:选位置 时,串外有两个 0 可自由取舍,贡献 ;选位置 时,串外只有一个 0 可选,贡献 ,总和为
样例 1 第 2 组 只存在一个 1,而 ,于是这颗 1 必选,其余三个 0 都可以自由保留或删除,答案是

题解

先只关心原串里所有 1 的位置

设原串中 1 的位置依次是:

如果一个合法子序列里选中了若干个 1,由于题目要求“任意相邻的两个 1 中间不能再有别的 1”,那么这些被选中的 1 一定对应着原串里的一段连续位置块

并且这段块的长度必须恰好是

所以,合法方案只可能来自“从所有 1 里选出一段连续的 个”。

选定这段 1 之后,哪些 0 还能自由选

假设当前选择的是:

那么:

  • 这段内部的 1 必须全部选中;
  • 这些 1 之间夹着的 0 不能被选,否则子序列中的 1 就不会连续;
  • 段外的 0 都可以随便选或不选,因为它们只会出现在整段 1 的左边或右边。

因此,答案贡献只和“段外一共有多少个 0”有关。

计算一段连续 1 的贡献

设当前块的左端是第 1,右端是第 1

块左边可自由选择的 0 数量是:

因为在位置 之前一共有 个字符,其中恰好有 个是 1

同理,块右边可自由选择的 0 数量是:

于是总贡献就是:

把所有长度为 的连续 1 块都枚举一遍求和即可。

复杂度分析

先扫一遍字符串找出所有 1 的位置,再枚举每个长度为 的连续块。

  • 时间复杂度:
  • 空间复杂度:

参考代码

  • Python
import sys

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


def solve():
    t = int(input())
    out = []

    for _ in range(t):
        n, m = map(int, input().split())
        s = input()

        pos = [i + 1 for i, ch in enumerate(s) if ch == "1"]
        k = len(pos)

        if k < m:
            out.append("0")
            continue

        pow2 = [1] * (n + 1)
        for i in range(1, n + 1):
            pow2[i] = (pow2[i - 1] * 2) % MOD

        ans = 0
        for l in range(k - m + 1):
            r = l + m - 1
            left_zero = pos[l] - (l + 1)
            right_zero = (n - pos[r]) - (k - (r + 1))
            ans += pow2[left_zero + right_zero]
            if ans >= MOD:
                ans -= MOD

        out.append(str(ans))

    sys.stdout.write("\n".join(out))


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

static const int MOD = 998244353;

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

    int T;
    cin >> T;
    while (T--) {
        int n, m;
        string s;
        cin >> n >> m >> s;

        vector<in

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

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

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

全部评论

相关推荐

评论
点赞
收藏
分享

创作者周榜

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