夜雨聆风学习资料网

ARTICLE · 1059978

拼多多9月22日机考笔试题与解析

拼多多9月22日机考笔试题与解析

写在前面

本次给大家带来2026年9月22日拼多多笔试题的4道题,本场机考题目可在咱们平台上在线刷题。

大厂笔试AI Coding练习:CodeFun2000.com/aicoder-gym

题号
题目
难度(对标leetcode)
核心做法
1
工位等量连搬
简单
贪心算法
2
双色条码最长保留
简单
模拟
3
货格最少框边
中等
二分
4
开合区间校验
困难
线段树

第1题-工位等量连搬

题目内容

装配车间里,今晚要处理的工位排成一行,一共  个。第  个工位待下线的零件个数是 

夹具每次勾取时,只能框住下标连续的一段 ,并且这段里各个工位的零件个数必须彼此相等,对不齐就不能勾。被勾中的工位只是零件清零,工位仍留在原处,因此左右两段不会贴合。

每个工位上的零件必须整批勾走,不能拆成两次。请给出清空整行所需的最少勾取次数。

题解

解题思路

夹具一次只能框住原序列上一段零件个数完全相同的连续工位。工位本身不撤走,中间清空后左右也不会贴到一起。

  1. 每个工位都要被勾到,而且同一工位的零件必须整批勾走,所以每一段被勾中的区间都落在某一个极大等值段内部。
  2. 把一个极大等值段拆成多次勾,次数只会更多。因此每一段恰好勾一次是最优的。
  3. 左右两段零件个数相同,只要中间还隔着别的个数,就不能并进同一次。答案等于从左到右扫出来的段数。
  4. 实现时维护上一个工位的零件个数,当前值不同就把答案加一。只有一个工位时答案是 

复杂度分析

  • 时间复杂度:。每组询问把该行工位扫一遍。
  • 空间复杂度:。存当前这一行的零件个数。

代码实现

python代码(C++和JAVA代码见在线OJ网址)

defcount_runs(xs):# 工位留在原位,不相邻的同值不会贴合,答案就是极大等值段的段数ifnot xs:return0    runs = 1for i in range(1, len(xs)):# 和左边零件个数不同,就必须新开一次勾取if xs[i] != xs[i - 1]:            runs += 1return runsdefsolve_cases(cases):# 每组询问单独数段,互不影响return [count_runs(xs) for xs in cases]defmain():# 首行是询问条数,随后每行先是工位数,再跟各工位零件个数    q = int(input())    cases = []for _ in range(q):        row = list(map(int, input().split()))        m = row[0]        cases.append(row[1:1 + m])    ans = solve_cases(cases)# 全部答案写在同一行,空格分隔    print(" ".join(str(x) for x in ans))if __name__ == "__main__":    main()

第2题-双色条码最长保留

题目内容

质检台送来一条双色墨点条码。每颗墨点要么是浅色,要么是深色:浅色记成字母 ,深色记成字母 

把一条条码叫作合格条码,意思是它能从左到右切成三段相接的部分,并且:

靠前那一段里,出现过的墨点全都是浅色 

正中那一段里,出现过的墨点全都是深色 

靠后那一段里,出现过的墨点全都是浅色 

三段都允许一颗墨点都没有。

换种说法:浅色只能堆在深色块的两侧,深色自己连成中间那一块。

质检员可以擦掉任意几颗墨点,一颗都不擦也行。擦完以后,留下的墨点还得保持原来从左往右的相对次序。请算出擦完后,合格条码最多能留下几颗墨点。

墨点个数  满足 

题解

解题思路

留下的墨点必须仍按原顺序排成「浅色段 + 深色段 + 浅色段」,三段都允许为空。同一种颜色在自己那一段里全部留下不会更差,所以只要决定深色段覆盖原条码的哪一段下标。

  1. 设条码为 ,长度为 。选定切分点 ):下标落在  的位置只留  只留  只留 
  2. 用前缀计数  表示前  颗里浅色、深色各有多少。这一对切分的长度是,也就是  再加上只跟  有关的部分。
  3. 右端点  从  扫到 ,顺手维护  时  的最大值。每个  用这个最大值更新答案。 时深色段为空,全留浅色;整段都划进深色段时,两侧浅色可以为空。

复杂度分析

  • 时间复杂度:。前缀和扫一遍,右端点再扫一遍。
  • 空间复杂度:。保存两个前缀数组。

代码实现

python代码(C++和JAVA代码见在线OJ网址)

deflongest_keep(t, m):# 前缀里浅色 a、深色 b 各有多少颗,下标覆盖 0 到 m    cnt_a = [0] * (m + 1)    cnt_b = [0] * (m + 1)for i in range(m):        cnt_a[i + 1] = cnt_a[i]        cnt_b[i + 1] = cnt_b[i]if t[i] == "a":            cnt_a[i + 1] += 1else:            cnt_b[i + 1] += 1    total_a = cnt_a[m]# best_diff:左端点不超过当前右端点时,左段 a 个数减去左段 b 个数的最大值    best_diff = -10**18    ans = 0for j in range(m + 1):        diff = cnt_a[j] - cnt_b[j]if diff > best_diff:            best_diff = diff# 右端点定在 j:左段用最优切分,中段把区间里的 b 全部留下,右段把后面的 a 全部留下        cur = best_diff + cnt_b[j] + (total_a - cnt_a[j])if cur > ans:            ans = curreturn ansdefmain():# 第一行是墨点数,第二行是条码    m = int(input())    t = input().strip()    print(longest_keep(t, m))if __name__ == "__main__":    main()

第3题-货格最少框边

题目内容

冷链仓被切成边长为  的方格。仓内摆着  件货,每件独占一格,位置用列号、行号标出。

巡检只允许框一次。这个框是边长为  的正方形,里面正好盖住  个完整方格,框中的货会一次读完。框越大越费电。请在读到的货不少于  件的前提下,求出最小的 

题解

解题思路

方格列号、行号都在  到  之间。边长为  的框就是连续  列、连续  行。 越大越容易凑够  件,因此可以二分最小的 

  1. 把每件货记到  的方格表上。货的位置互不相同。
  2. 做二维前缀和。查询任意连续列、连续行里的件数是 
  3. 检查边长  时,枚举框的左上角。闭区间列 、行  的件数不少于 ,这个边长就可行。
  4. 若选中若干件货,列号最小最大是 ,行号最小最大是 ,盖住它们的最小边长是 。所以答案落在  到  之间,而且最优框可以贴着这些货,不会伸到编号范围外面。
  5. 只需要  件时,答案直接是 

复杂度分析

  • 时间复杂度:,其中 。前缀和先花 ,二分大约  轮,每一轮最坏扫描  个左上角。
  • 空间复杂度:。方格表和前缀和都是这个量级。

货的件数最多 ,判定过程只跟方格边长上限  有关。

代码实现

python代码(C++和JAVA代码见在线OJ网址)

V = 1000defmin_side(k, cols, rows):# 只要求框到 1 件时,任意一格的边长都是 1if k <= 1:return1    width = V + 1# 列、行都从 1 编号,把每件货记到对应方格    grid = [0] * (width * width)for c, r in zip(cols, rows):        grid[c * width + r] = 1# ps[c][r] 表示列 1..c、行 1..r 这一块里的件数    ps = [0] * (width * width)for c in range(1, V + 1):        running = 0        cur = c * width        prev = (c - 1) * widthfor r in range(1, V + 1):            running += grid[cur + r]            ps[cur + r] = ps[prev + r] + runningdefenough(side):# 枚举左上角,统计边长为 side 的闭区间里有多少件货        span = side - 1        last = V - spanfor c in range(1, last + 1):            hi = (c + span) * width            lo = (c - 1) * widthfor r in range(1, last + 1):                r2 = r + span                total = ps[hi + r2] - ps[hi + r - 1] - ps[lo + r2] + ps[lo + r - 1]if total >= k:returnTruereturnFalse# 边长越大越容易凑够 k 件,二分最小可行边长    low, high = 1, Vwhile low < high:        mid = (low + high) // 2if enough(mid):            high = midelse:            low = mid + 1return lowdefmain():    k = int(input())    p = int(input())    cols = list(map(int, input().split()))    rows = list(map(int, input().split()))    print(min_side(k, cols, rows))if __name__ == "__main__":    main()

第4题-开合区间校验

题目内容

一条报文写成字符串 ,一共  个字符。每个字符要么是开符 [,要么是合符 ]

调度一共做  次动作,只有下面两类。

第一类给出两端 ,把第  个到第  个字符全部换成对面:[ 换成 ]] 换成 [

第二类给出两端 ,问当前这一段是否配平。

配平的含义:从左往右扫描这一段,遇到 [ 就让计数加 ,遇到 ] 就让计数减 。扫描途中计数始终不能小于 ,扫到末尾时计数必须回到 

例如 [][[]][][] 都配平;][[[]][][ 都不配平。

题解

解题思路

一段报文配平,等价于两件事同时成立:从左端点起的前缀和始终不小于 ,并且整段的和等于 。开符贡献 ,合符贡献 

  1. 建一棵线段树。每个节点记下这段的和、最小前缀和、最大前缀和。
  2. 左右两段合并时,和直接相加。最小前缀要么停在左边,要么是左边的和再加上右边的最小前缀。最大前缀同样处理。
  3. 把一整段开合对调,每个数都变号。于是区间和取反,原来的最大前缀变成新的最小前缀(再加一个负号),原来的最小前缀变成新的最大前缀。节点上挂一个懒标记,标记再遇到一次就抵消。
  4. 修改或询问之前,先把路径上的懒标记下传,避免父亲的对调和孩子的数值打架。
  5. 询问时取出覆盖  的若干段,按从左到右拼出和与最小前缀。和为  且最小前缀不小于  就输出 ,否则输出 。对调操作不输出。

复杂度分析

  • 时间复杂度:。每次对调或询问只碰到  个节点。
  • 空间复杂度:。线段树按报文长度的常数倍开。

 和  最大都是 

代码实现

python代码(C++和JAVA代码见在线OJ网址)

defsolve(w, ops):# 开符记 +1,合符记 -1。配平 <=> 区间和为 0 且最小前缀和 >= 0    n = len(w)    base = 1while base < n:        base <<= 1    height = base.bit_length() - 1# 叶子放在 base 起的位置,凑不满的叶子保持 0,询问范围不会落到那里    sm = [0] * (base << 1)    mn = [0] * (base << 1)    mx = [0] * (base << 1)    lz = [0] * (base << 1)for i, ch in enumerate(w):        v = 1if ch == "["else-1        leaf = base + i        sm[leaf] = mn[leaf] = mx[leaf] = vfor p in range(base - 10-1):        left = p << 1        right = left | 1        ls = sm[left]        sm[p] = ls + sm[right]        mn[p] = mn[left] if mn[left] < ls + mn[right] else ls + mn[right]        mx[p] = mx[left] if mx[left] > ls + mx[right] else ls + mx[right]    ans = []for op, a, b in ops:if op == 1:# 对调 [a, b]:先把路径上的懒标记推下去,再覆盖这一段,最后向上重算# 1 起编号的闭区间 [a, b] 对应叶子 [base+a-1, base+b)            left = base + a - 1            right = base + b            idx = leftfor shift in range(height, 0-1):                p = idx >> shiftif lz[p]:                    c = p << 1                    sm[c] = -sm[c]                    mn[c], mx[c] = -mx[c], -mn[c]                    lz[c] ^= 1                    c |= 1                    sm[c] = -sm[c]                    mn[c], mx[c] = -mx[c], -mn[c]                    lz[c] ^= 1                    lz[p] = 0            idx = right - 1for shift in range(height, 0-1):                p = idx >> shiftif lz[p]:                    c = p << 1                    sm[c] = -sm[c]                    mn[c], mx[c] = -mx[c], -mn[c]                    lz[c] ^= 1                    c |= 1                    sm[c] = -sm[c]                    mn[c], mx[c] = -mx[c], -mn[c]                    lz[c] ^= 1                    lz[p] = 0            begin_l = left            begin_r = rightwhile left < right:if left & 1:                    sm[left] = -sm[left]                    mn[left], mx[left] = -mx[left], -mn[left]                    lz[left] ^= 1                    left += 1if right & 1:                    right -= 1                    sm[right] = -sm[right]                    mn[right], mx[right] = -mx[right], -mn[right]                    lz[right] ^= 1                left >>= 1                right >>= 1# 父亲若仍挂着对调标记,用孩子重算后再把标记作用回去            idx = begin_lwhile idx > 1:                idx >>= 1                leftc = idx << 1                rightc = leftc | 1                ls = sm[leftc]                sm[idx] = ls + sm[rightc]                mn[idx] = mn[leftc] if mn[leftc] < ls + mn[rightc] else ls + mn[rightc]                mx[idx] = mx[leftc] if mx[leftc] > ls + mx[rightc] else ls + mx[rightc]if lz[idx]:                    sm[idx] = -sm[idx]                    mn[idx], mx[idx] = -mx[idx], -mn[idx]            idx = begin_r - 1while idx > 1:                idx >>= 1                leftc = idx << 1                rightc = leftc | 1                ls = sm[leftc]                sm[idx] = ls + sm[rightc]                mn[idx] = mn[leftc] if mn[leftc] < ls + mn[rightc] else ls + mn[rightc]                mx[idx] = mx[leftc] if mx[leftc] > ls + mx[rightc] else ls + mx[rightc]if lz[idx]:                    sm[idx] = -sm[idx]                    mn[idx], mx[idx] = -mx[idx], -mn[idx]else:# 询问 [a, b] 的区间和与最小前缀和# 1 起编号的闭区间 [a, b] 对应叶子 [base+a-1, base+b)            left = base + a - 1            right = base + b            idx = leftfor shift in range(height, 0-1):                p = idx >> shiftif lz[p]:                    c = p << 1                    sm[c] = -sm[c]                    mn[c], mx[c] = -mx[c], -mn[c]                    lz[c] ^= 1                    c |= 1                    sm[c] = -sm[c]                    mn[c], mx[c] = -mx[c], -mn[c]                    lz[c] ^= 1                    lz[p] = 0            idx = right - 1for shift in range(height, 0-1):                p = idx >> shiftif lz[p]:                    c = p << 1                    sm[c] = -sm[c]                    mn[c], mx[c] = -mx[c], -mn[c]                    lz[c] ^= 1                    c |= 1                    sm[c] = -sm[c]                    mn[c], mx[c] = -mx[c], -mn[c]                    lz[c] ^= 1                    lz[p] = 0            left_parts = []            right_parts = []while left < right:if left & 1:                    left_parts.append(left)                    left += 1if right & 1:                    right -= 1                    right_parts.append(right)                left >>= 1                right >>= 1            total = 0            best = Nonefor p in left_parts:                cand = total + mn[p]if best isNoneor cand < best:                    best = cand                total += sm[p]for j in range(len(right_parts) - 1-1-1):                p = right_parts[j]                cand = total + mn[p]if best isNoneor cand < best:                    best = cand                total += sm[p]            ans.append(1if total == 0and best >= 0else0)return ansdefmain():    m = int(input())    t = int(input())    w = input().strip()    ops = []for _ in range(t):        op, a, b = map(int, input().split())        ops.append((op, a, b))for bit in solve(w, ops):        print(bit)if __name__ == "__main__":    main()

相关学习资料