登录
注册
忘记密码
忘记密码? 还没有账号?立即注册
已有账号?去登录

【已坠机】pdd服务端工程师笔试0823

5 4 发布时间: 2026/08/23 21:26 上次更新: 2026/08/23 21:37
作者: 有地 丛雨 绫 LV.1
Java 拼多多 笔试 秋招

封面
封面图片
点击查看原图

先说说,27届,知道自己算法不咋好,系统性刷题不久,但是头铁,二次挑战多多牛客笔试,战况:前两题我感觉是简单/中等贪心题,我没刷过,内容见图,通过88.89、28(加了个边界判断多了4%)

最后两题是困难级别的吧,我都没怎么看,记下来的就两道大家看图吧,内容和字迹比较潦草望各位见谅哈哈,祝秋招小伙伴们成功,我继续沉淀吧哈哈

pdd-0823笔试前两题

作者:我是苍苍 链接:https://www.nowcoder.com/feed/main/detail/7db592f634b64856a158083c0363178f?sourceSSR=users 来源:牛客网

新编辑(和语言模型Kimi K3讨论了下,感觉这是合适的解决方法):

第一题:数据中心巡检(灯状态反转)

你的思路(我理解的对吗?)

D[2][n]:D[0]存原始状态,D[1]存反转状态(即 1-a[i])
cur = 0 表示"当前相当于被翻转了偶数次"
从左到右遍历:
  如果 D[cur][i] == 0(当前灯是灭的)
    次数++
    cur ^= 1(切换奇偶状态,因为按了按钮 i,i~n 全部翻转)

这个思路完全正确! 你实际上是用 cur 的奇偶性代替了"当前位置被前面按过的按钮累计翻转了多少次",这是一个 O(n) 的优雅做法。

为什么可能是 88.9%?

最可能的坑是 数组越界或下标问题: 循环写成 0 n 而不是 0 n-1:如果循环到 i=n,访问了未初始化内存,恰好那个值是 0,就会多算一次按钮。 数组开在栈上且不够大:如果写成 int D[2][100000],而题目 n=100000,在 C++ 中栈空间可能爆掉(不同编译器栈大小不同),建议开全局或 vector。 输入格式:比如题目是 1-indexed 给出机柜编号,但你按 0-indexed 读入,导致错位。 标准写法(C++)

#include <bits/stdc++.h>
using namespace std;

int main() {
    ios::sync_with_stdio(false);
    cin.tie(nullptr);
    
    int n;
    if (!(cin >> n)) return 0;
    vector<int> a(n);
    for (int i = 0; i < n; i++) cin >> a[i];
    
    int ans = 0;
    int flip = 0;  // 0: 偶数次翻转, 1: 奇数次翻转
    
    for (int i = 0; i < n; i++) {
        int actual = a[i] ^ flip;  // 当前灯的实际状态
        if (actual == 0) {
            ans++;
            flip ^= 1;  // 按按钮 i,i~n 翻转,后续所有灯奇偶性改变
        }
    }
    
    cout << ans << "\n";
    return 0;
}

你的双数组切换 cur ^= 1 和这个 flip ^= 1 本质一模一样,只是我省去了开二维数组的空间。如果这题卡你了,大概率是边界细节而非思路问题。

第二题:修整长跑赛道(最大化最小平整度)

你的思路

重复 m 次:
  枚举所有长度为 k 的连续区间,计算区间和
  选区间和最小的那个区间,给区间内每个 ai += 1
最后输出全局最小值

这个贪心策略是错误的。 "每次选和最小的区间" 并不能保证最终全局最小值最大。 反例(帮你找到丢分原因)

n=4, m=2, k=2
a = [1, 1, 100, 1]

按你的贪心: 第1次:区间 [0,1] 和=2,[1,2] 和=102,[2,3] 和=101 → 选 [0,1] → [2,2,100,1] 第2次:区间 [0,1] 和=4,[1,2] 和=102,[2,3] 和=101 → 选 [0,1] → [3,3,100,1] 最终最小值 = 1(第4条道完全没修到) 但最优解: 第1次:[0,1] → [2,2,100,1] 第2次:[2,3] → [2,2,101,2] 最终最小值 = 2

贪心只看了"当前哪块最穷",但没看"哪块最需要被拉一把"。 这类让"最小值尽可能大"的问题,标准套路是二分答案。 正确思路:二分答案 + 差分验证 核心思想:假设我们想让最终每条道的平整度都 至少为 x,验证这个 x 是否能在 m 次操作内达成。

验证函数 check(x): 从左到右遍历每条道。 用差分数组维护"当前位置已经被前面的操作加了多少"。 如果第 i 条道实际值 < x,说明必须从 i 开始往后修,需要修 (x - 实际值) 次。 每次操作覆盖 [i, i+k-1],用差分在 i 处 +d,在 i+k 处 -d。 累加操作次数,如果超过 m 则 x 不可行。 二分范围:low = min(a),high = min(a) + m(或更大,比如 2e9)。 标准写法(C++)

#include <bits/stdc++.h>
using namespace std;
typedef long long ll;

int main() {
    ios::sync_with_stdio(false);
    cin.tie(nullptr);
    
    ll n, m, k;
    cin >> n >> m >> k;
    vector<ll> a(n);
    for (int i = 0; i < n; i++) cin >> a[i];
    
    ll left = *min_element(a.begin(), a.end());
    ll right = left + m;  // 上界,每次操作最多让某个位置+1
    ll ans = left;
    
    auto check = [&](ll x) -> bool {
        vector<ll> diff(n + 1, 0);  // 差分数组,多开一位防止 i+k 越界
        ll cur = 0;      // 当前位置累计被加了多少
        ll used = 0;     // 已用操作次数

        for (int i = 0; i < n; i++) {
            cur += diff[i];
            ll actual = a[i] + cur;
            if (actual < x) {
                ll need = x - actual;  // 需要从 i 开始修 need 次
                if (need < 0) continue;
                used += need;
                if (used > m) return false;  // 操作次数超了
                cur += need;
                if (i + k < n) diff[i + k] -= need;  // 差分:影响范围到 i+k-1
            }
        }
        return true;
    };

    while (left <= right) {
        ll mid = (left + right) / 2;
        if (check(mid)) {
            ans = mid;
            left = mid + 1;
        } else {
            right = mid - 1;
        }
    }

    cout << ans << "\n";
    return 0;
}

复杂度 check() 是 O(n),二分是 O(log(m)),总复杂度 O(n log m),轻松通过。


评论 (4)
评论

← 返回首页
封面原图
TOMATO 7co
TOMATO
7co
暂无歌词
00:00 00:00
Bass演奏动效
歌单