大厂真题 / 阿里巴巴

阿里 3.28 笔试真题 - 算法岗

本场考试概述

考试时间:2026年3月28日 考试岗位:算法岗 难度评级:中等

考点分析

  • 第一题:数学推导/博弈(难度简单)
  • 第二题:枚举/完全平方数判断(难度中等)
  • 第三题:排序 + 并查集(难度中等偏难)

建议策略

  • 第一题数学推导出 A-B 是常数,O(n) 秒杀
  • 第二题枚举所有完全平方数逐个检查切分点,注意前导零
  • 第三题离线排序 + 并查集合并,需要熟悉并查集模板

第一题:回合制游戏

题目描述

给定两个长度均为 n 的整数数组 a 与 b,进行一场回合制对抗游戏。Alice 先手,两人轮流选择未被选择的位置。Alice 选位置 i 净收益为 a[i]-b[i],Bob 选位置 j 净收益为 b[j]-a[j]。

Alice 希望最大化 A-B,Bob 希望最小化 A-B,双方完全理性。求最优博弈下的分差 A-B。

样例

输入

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

输出

0
0
-5

题解:数学推导

无论双方如何选择,A - B 恒等于 sum(a) - sum(b),是一个与策略无关的常数。直接计算两个数组的元素和之差即可。

时间复杂度:O(n)。

import sys
input = sys.stdin.readline

def main():
    T = int(input())
    out = []
    for _ in range(T):
        n = int(input())
        a = list(map(int, input().split()))
        b = list(map(int, input().split()))
        out.append(str(sum(a) - sum(b)))
    print("\n".join(out))

main()

第二题:完全平方数

题目描述

定义”完完全全平方数”:该数本身是完全平方数,且存在一个切分点将十进制表示切为两段,两段均非空且都是完全平方数(不允许前导零,除非该段仅为 0)。

给定上界 n,问不超过 n 的完完全全平方数共有多少个。

样例

输入

1
1500

输出

5

题解:枚举

n <= 10^9,完全平方数最多约 31623 个,直接枚举每个完全平方数并检查所有切分点。

时间复杂度:O(sqrt(n) * D),D 为数字位数(最多 10)。

import sys
input = sys.stdin.readline
from math import isqrt

def is_perfect_square(x):
    if x < 0:
        return False
    s = isqrt(x)
    return s * s == x

def check(x):
    s = str(x)
    for j in range(1, len(s)):
        left, right = s[:j], s[j:]
        if len(left) > 1 and left[0] == '0':
            continue
        if len(right) > 1 and right[0] == '0':
            continue
        if is_perfect_square(int(left)) and is_perfect_square(int(right)):
            return True
    return False

def main():
    T = int(input())
    out = []
    for _ in range(T):
        n = int(input())
        count = sum(1 for i in range(1, isqrt(n) + 1) if check(i * i))
        out.append(str(count))
    print("\n".join(out))

main()

第三题:弦上生花

题目描述

给定由 n 个整数构成的数组 a,对于每个整数 k (1 <= k <= n),求最长连续子数组使得所有相邻元素差的绝对值不超过 k。

样例

输入

1
5
1 3 5 2 4

输出

1 3 5 5 5

题解:排序 + 并查集

令 d[i] = a[i+1] - a[i] ,随着 k 增大,越来越多的 d[i] 被”激活”,相邻段合并变长。按 d 值从小到大排序后逐步用并查集合并,维护最大连通块。

时间复杂度:O(n log n)。

import sys
input = sys.stdin.readline

def main():
    T = int(input())
    out = []
    for _ in range(T):
        n = int(input())
        a = list(map(int, input().split()))
        if n == 1:
            out.append("1")
            continue

        par = list(range(n))
        sz = [1] * n

        def find(x):
            while par[x] != x:
                par[x] = par[par[x]]
                x = par[x]
            return x

        def unite(x, y):
            px, py = find(x), find(y)
            if px == py:
                return sz[px]
            if sz[px] < sz[py]:
                px, py = py, px
            par[py] = px
            sz[px] += sz[py]
            return sz[px]

        edges = sorted((abs(a[i + 1] - a[i]), i) for i in range(n - 1))

        ans = [0] * (n + 1)
        max_sz, ei = 1, 0
        for k in range(1, n + 1):
            while ei < n - 1 and edges[ei][0] <= k:
                max_sz = max(max_sz, unite(edges[ei][1], edges[ei][1] + 1))
                ei += 1
            ans[k] = max_sz
        out.append(" ".join(str(ans[k]) for k in range(1, n + 1)))
    print("\n".join(out))

main()

小结

  • 第一题博弈看似复杂,数学推导后发现 A-B 是常数 sum(a)-sum(b)
  • 第二题枚举所有完全平方数并检查切分点,注意前导零判断
  • 第三题离线排序 + 并查集是经典套路:随阈值递增逐步合并,维护最大连通块