OA辅助 / 面试训练复盘

OpenAI OA Coding 面试复盘|状态压缩 DP、矩阵模拟与区间 DP 四题

这套 OpenAI OA 用 CoderPad 平台,限时两小时,四道 Coding 题分别落在状态压缩 DP、矩阵模拟、图像处理与区间 DP 上。两道 DP 最费思考,两道模拟题则要盯紧移动顺序、边界和结果矩阵不能边算边覆盖。

OpenAI OA 四道 Coding 题图解:状态压缩 DP、矩阵模拟、图像处理与区间 DP
训练主题配图 · 非客户现场证明

这套 OpenAI OA 用 CoderPad 平台,限时两小时,一共四道 Coding 题:四类商店路径计数、风向推动矩阵、图像镜像与模糊,以及两人从牌堆两端取牌。整体看下来,两道 DP 最费思考,两道模拟题的分则容易丢在移动顺序、边界判断和结果矩阵的更新时机上。下面按题目拆开讲思路和最容易写错的地方。

第一题:经过四类商店的路径计数

题目描述

给定商店类型数组 shops 和道路列表 roads,每个商店属于 0 到 3 中的一类。统计沿道路连续经过商店的路径数量,要求四种类型各出现一次,且不能重复经过同一类型。题目问的是路径数量,不是最短距离。

解题思路

先按道路连接关系建立邻接表,用四位二进制记录已经经过的商店类型。dp[mask][v] 表示以 v 结尾、已访问类型为 mask 的路径数量,初始时每个商店都能作为起点。沿道路转移到下一家商店时,只有目标商店的类型尚未出现才更新状态,最后汇总 mask 为 15 的计数。

def count_type_paths(shops, roads):
    n = len(shops)
    graph = [[] for _ in range(n)]
    for u, v in roads:
        graph[u].append(v)
        graph[v].append(u)

    full = (1 << 4) - 1
    dp = [[0] * n for _ in range(1 << 4)]
    for v in range(n):
        dp[1 << shops[v]][v] = 1

    for mask in range(1, 1 << 4):
        for v in range(n):
            if dp[mask][v] == 0:
                continue
            for w in graph[v]:
                bit = 1 << shops[w]
                if mask & bit:
                    continue
                dp[mask | bit][w] += dp[mask][v]

    return sum(dp[full])

商店类型只有四种,mask 只有 16 个取值,状态空间很小;时间复杂度 O(16·(n+m)),额外空间 O(16·n)。最容易漏的是起点初始化:每个商店都要先把自己这一类型放进 mask,否则会少算从任意节点出发的路径。

第二题:风向推动矩阵元素

题目描述

给定一个二维矩阵,每个位置表示该格的树叶数量,以及由 U、D、R、L 组成的风向字符串。每次风把所有仍在矩阵内的树叶向对应方向移动一格,移出边界的树叶丢弃。按顺序执行全部操作后,返回剩余树叶的总数。

解题思路

把 U、D、R、L 映射成上下左右四个坐标偏移。每次操作新建一个全零矩阵,遍历当前矩阵,把仍在边界内的元素搬到新位置,越界的直接舍弃,再用新矩阵执行下一次操作。已经移出的元素不会因为后续反向风而恢复,所以不能只算四个方向的净位移。

def remaining_leaves(grid, winds):
    dirs = {'U': (-1, 0), 'D': (1, 0), 'L': (0, -1), 'R': (0, 1)}
    rows, cols = len(grid), len(grid[0])
    cur = [row[:] for row in grid]
    for w in winds:
        dr, dc = dirs[w]
        nxt = [[0] * cols for _ in range(rows)]
        for r in range(rows):
            for c in range(cols):
                if cur[r][c] == 0:
                    continue
                nr, nc = r + dr, c + dc
                if 0 <= nr < rows and 0 <= nc < cols:
                    nxt[nr][nc] += cur[r][c]
        cur = nxt
    return sum(sum(row) for row in cur)

每一步都是全体元素同方向平移,越界即丢弃。时间复杂度 O(L·R·C),L 为风向长度,R、C 为矩阵尺寸。这里最容易漏的是每次都要用上一轮的新矩阵继续算,不能反复基于最初的矩阵累加。

第三题:图像镜像与邻域模糊

题目描述

给一个像素值在 0 到 255 之间的二维图像矩阵,根据指定滤镜执行左右镜像或模糊。镜像以竖直轴为对称轴;模糊时取原图中周围八个方向的有效邻居,计算平均值并四舍五入,不包含当前像素,没有邻居时保留原值。

解题思路

镜像时逐行反转,每个像素从列 c 移到 width - 1 - c。模糊时新建结果矩阵,逐个像素枚举八个方向,只累加原图中未越界的邻居,再按实际邻居数求平均并四舍五入。不能边计算边覆盖原图,否则后续像素会读到已经修改的值。

def mirror(image):
    return [row[::-1] for row in image]


def blur(image):
    rows, cols = len(image), len(image[0])
    result = [[0] * cols for _ in range(rows)]
    for r in range(rows):
        for c in range(cols):
            total, count = 0, 0
            for dr in (-1, 0, 1):
                for dc in (-1, 0, 1):
                    if dr == 0 and dc == 0:
                        continue
                    nr, nc = r + dr, c + dc
                    if 0 <= nr < rows and 0 <= nc < cols:
                        total += image[nr][nc]
                        count += 1
            result[r][c] = round(total / count) if count else image[r][c]
    return result

模糊必须在原图上读、在结果矩阵上写。边角像素按实际邻居数求平均,单像素图像没有邻居,保留原值即可。

第四题:两端取牌的最优得分

题目描述

桌上有偶数张牌,每张牌的分值都是正整数。两名玩家轮流从剩余牌的最左端或最右端取一张,取到的分值加入自己的总分。双方都知道整副牌的顺序,并采用最优策略,要求返回先手玩家的最终得分。

解题思路

用 dp[l][r] 表示面对区间 [l, r] 时,当前玩家能比对手多拿的最大分数。取左端后分差为 cards[l] - dp[l+1][r],取右端后为 cards[r] - dp[l][r-1],取两者较大值。从短区间算到长区间,最后用「牌面总和加最终分差,再除以 2」得到先手得分。

def first_player_score(cards):
    n = len(cards)
    dp = [[0] * n for _ in range(n)]
    for length in range(1, n + 1):
        for l in range(n - length + 1):
            r = l + length - 1
            if length == 1:
                dp[l][r] = cards[l]
            else:
                take_left = cards[l] - dp[l + 1][r]
                take_right = cards[r] - dp[l][r - 1]
                dp[l][r] = max(take_left, take_right)
    return (sum(cards) + dp[0][n - 1]) // 2

当前拿哪一端,会决定对手接下来能拿到什么,所以每次贪心选两端较大值并不成立;区间 DP 会同时计算对手的最优回应。时间复杂度 O(n²),空间可压缩到 O(n)。

备考建议

两道 DP 建议先手推小样例再写代码:第一题确认 mask 的初始化与转移条件,第四题确认区间长度递增的循环顺序。两道模拟题建议先写正确版本,再补「越界丢弃」「结果矩阵与原始矩阵分离」这类边界测试,不要一上来追求最简写法。

时间上,四题里 DP 通常更花思考,模拟题则要留出检查细节的时间。每写完一题,跑一组正常样例和一组极端样例再进入下一题。

FAQ

状态压缩 DP 的 mask 为什么只用四位?

因为题目要求四种类型各出现一次,四位二进制恰好能记录每种类型是否已经出现,总共只有 16 个状态。相比记录每个节点是否访问过,状态数从指数级降到常数级。

风向模拟能只算四个方向的净位移吗?

不能。树叶中途越界就被丢弃,即使后面的风向把位置移回来,丢失的树叶也不会恢复,所以必须逐步模拟每一步。

模糊计算需要包含当前像素吗?

不包含。只统计周围八个方向中未越界的邻居,边角像素按实际邻居数求平均,单像素图像保留原值。

取牌时每次选两端较大的牌可以吗?

不可以。当前拿哪一端,会决定对手接下来能拿到什么;区间 DP 会同时计算对手的最优回应,贪心只看眼前两步并不成立。

参考来源

OpenAI OA 四道 Coding 题的候选人分享与平台记录 OpenAI Software Engineer 面试流程与 CoderPad 测评说明 OpenAI Software Engineer 面试问题与准备要点

联系 VOPathway

如果你希望把这套方法用于自己的 OA 或 VO 准备,可以把目标岗位、当前阶段、面试时间和主要难点发给 VOPathway,我们会据此安排针对性的练习与复盘。

  • 微信:Coding0201
  • 邮箱:[email protected]
  • Telegram:@OAVOProxy
  • WhatsApp:+86 178 6396 8105

可提供 OA辅助、OA辅导、VO辅助与 VO辅导。文章用于训练参考,不代表相关公司的官方流程;实际安排请以候选人收到的通知为准。

每一步,都更接近准备充分的自己。

本文用于面试准备与训练复盘。公司名称仅用于标识题型素材主题,不代表官方合作或录用承诺。

预约一次训练复盘 ↗