【笔试刷题】VIVO-2026.03.13-改编真题

✅ 春招备战指南 ✅

💡 学习建议:

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

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

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

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

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

VIVO-2026.03.13

这套 2026.03.13 的 VIVO 题节奏很清楚。第一题是标准状态机动态规划,只是把“卖出后冷静 k 天”这个限制推广成了可变冷静期;第二题重在把圆环规则读准,数据范围不大时直接模拟反而最稳;第三题的核心不是选区间,而是固定区间后怎样把严格递增的分配总量压到最大。

题目一:补货窗口收益规划

这题本质上还是“持有 / 不持有”两种状态。真正需要注意的是买入时不能直接接昨天的空仓状态,而要回退到 i-k-1 这一天。把这个转移写对以后,整题就是一遍线性 DP。

题目二:幸运花环淘汰赛

题面看着有点绕,其实就是一个会动态换步长的圆环淘汰游戏。重点不是上复杂数据结构,而是把“从谁开始走、走满之后删谁、删完下一轮从哪里开始”三件事说清楚。题目只给到 n <= 1000,直接模拟比硬上花哨结构更稳。

题目三:连续项目预算爬坡计划

这题最容易误判成“随便贪一个区间”。实际上连续区间和严格递增两个条件绑在一起后,固定右端点、从右向左贪心才是最自然的切法。右边尽量大,左边才有空间跟着抬起来,所以用 min(上限, 右边分配值 - 1) 反推即可。

1. 补货窗口收益规划

问题描述

L先生负责一家潮玩仓库的调仓工作。仓库每天都会给出一个进货价格数组 prices,如果当天买入一件商品,以后可以在某一天卖出获利。

不过流程上有几条限制:

  • 任意时刻最多只能持有一件商品。
  • 每次卖出之后,必须再等待 k 天,才能重新买入。
  • 可以做很多次交易,但每次交易都必须先买后卖,不能同时进行多笔交易。

给定每天的价格和冷静期 k,请计算最多可以获得多少利润。

输入格式

第一行输入两个整数 nk,分别表示天数和卖出后的冷静期。

第二行输入 n 个整数 prices[i],表示第 i 天的价格。

输出格式

输出一个整数,表示最多可以获得的利润。

样例输入

7 1
1 3 5 2 6 4 1
5 1
1 2 3 0 2
1 1
1

样例输出

6
3
0

数据范围

  • 1 <= n <= 200000
  • 0 <= k <= 200000
  • 0 <= prices[i] <= 10^9
样例编号 解释说明
样例 1 第 0 天买入,第 1 天卖出,利润为 2。因为冷静期是 1 天,所以第 2 天不能买入。接着第 3 天买入、第 4 天卖出,再赚 4。总利润为 6
样例 2 一种最优方案是第 0 天买入、第 1 天卖出,利润为 1。第 2 天进入冷静期,随后第 3 天买入、第 4 天卖出,再赚 2。总利润为 3
样例 3 只有一天,无法完成一笔完整交易,所以利润为 0

题解

这题表面是在枚举买卖时机,真正难点在于“卖出后不能立刻再买”这个限制。只要把“当天结束后手里有没有货”拆成两个状态,问题就会清楚很多。

设:

  • cash[i] 表示处理完第 i 天后,手里没有商品时能得到的最大利润。
  • hold[i] 表示处理完第 i 天后,手里持有一件商品时能得到的最大利润。

先看 cash[i]

当天结束时手里没货,只可能来自两种情况:

  • 昨天就没货,今天什么都不做。
  • 昨天手里有货,今天把它卖掉。

所以:

cash[i] = max(cash[i-1], hold[i-1] + prices[i])

再看 hold[i]

当天结束时手里有货,也有两种可能:

  • 昨天已经持有,今天继续拿着。
  • 今天刚买入。

关键是第二种情况。今天如果要买入,上一次“不持有”的状态必须至少停在第 i-k-1 天,因为卖出后还要空出 k 天冷静期。所以:

hold[i] = max(hold[i-1], cash[i-k-1] - prices[i])

i-k-1 < 0 时,说明之前还没有形成有效交易,买入就等价于从利润 0 开始。

整个转移只扫一遍数组,时间复杂度是 O(n),空间复杂度是 O(n)。这组数据范围下完全够用。

参考代码

  • Python
import sys
input = lambda: sys.stdin.readline().strip()

def solve():
    n, k = map(int, input().split())
    a = list(map(int, input().split()))

    # cash[i]:第 i 天结束后手里没货时的最大利润
    # hold[i]:第 i 天结束后手里有货时的最大利润
    cash = [0] * n
    hold = [0] * n
    hold[0] = -a[0]

    for i in range(1, n):
        # 今天结束时不持有:要么延续昨天不持有,要么今天卖出
        cash[i] = max(cash[i - 1], hold[i - 1] + a[i])

        # 今天想买入时,上一段不持有状态最多只能接到 i-k-1
        base = 0
        if i - k - 1 >= 0:
            base = cash[i - k - 1]

        # 今天结束时持有:要么继续持有,要么今天刚买
        hold[i] = max(hold[i - 1], base - a[i])

    print(cash[-1])

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

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

    int n, k;
    cin >> n >> k;
    vector<long long> a(n);
    for (int i = 0; i < n; ++i) {
        cin >> a[i];
    }

    // cash[i]:第 i 天结束后手里没货的最好利润
    // hold[i]:第 i 天结束后手里有货的最好利润
    vector<long long> cash(n, 0), hold(n, 0);
    hold[0] = -a[0];

    for (int i = 1; i < n; ++i) {
        // 卖出或者不动
        cash[i] = max(cash[i - 1], hold[i - 1] + a[i]);

        long long base = 0;
        if (i - k - 1 >= 0) {
            base = cash[i - k - 1];
        }

        // 持续持有或者今天买入
        hold[i] = max(hold[i - 1], base - a[i]);
    }

    cout << cash[n - 1] << '\n';
    return 0;
}
  • Java
import java.io.*;
import java.util.*;

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

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

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

            long sign = 1;
            if (c == '-') {
                sign = -1;
                c = read();
            }

            long val = 0;
            while (c > ' ') {
                val = val * 10 + c - '0';
                c = read();
            }
            return val * sign;
        }
    }

    public static void main(String[] args) throws Exception {
        FastScanner fs = new FastScanner();
        int n = (int) fs.nextLong();
        int k = (int) fs.nextLong();
        long[] a = new long[n];
        for (int i = 0; i < n; i++) {
            a[i] = fs.nextLong();
        }

        // cash[i]:第 i 天结束后没有持仓
        // hold[i]:第 i 天结束后仍然持仓
        long[] cash = new long[n];
        long[] hold = new long[n];
        hold[0] = -a[0];

        for (int i = 1; i < n; i++) {
            cash[i] = Math.max(cash[i - 1], hold[i - 1] + a[i]);

            long base = 0;
            if (i - k - 1 >= 0) {
                base = cash[i - k - 1];
            }

            hold[i] = Math.max(hold[i - 1], base - a[i]);
        }

        System.out.println(cash[n - 1]);
    }
}

2. 幸运花环淘汰赛

问题描述

n 名玩家围成一个圆环,按逆时针方向依次编号为 0,1,2,...,n-1。每名玩家还有一个固定的幸运值 lucky[i]

游戏从编号为 start 的玩家开始。当前步长一开始是 k,每一轮都按下面的规则进行:

  1. 从当前起点出发,沿逆时针方向依次向前走。
  2. 每经过一名仍在场的玩家,步数加 1
  3. 当累计走满当前步长时,落到的那名玩家出局。
  4. 下一轮从这名出局玩家的下一个仍在场玩家继续开始。
  5. 下一轮的步长会更新为这名出局玩家自己的幸运值。

当只剩下一名玩家时,游戏结束,请输出最终幸运者的编号。

特别地,当 n = 1 时,场上只有编号 0 的玩家,游戏无需进行任何轮次,答案就是 0

输入格式

第一行输入三个整数 nstartk,分别表示玩家人数、初始起点编号和初始步长。

第二行输入 n 个整数 lucky[i],表示每名玩家的幸运值。

输出格式

输出一个整数,表示最后留下来的幸运者编号。

样例输入

3 0 1
1 2 3

样例输出

0

数据范围

  • 1 <= n <= 1000
  • 0 <= start < n
  • 1 <= k <= 1000
  • 1 <= lucky[i] <= 1000
样例编号 解释说明
样例 1 初始起点是 0,步长为 1。先走到 1 号玩家,1 号出局,下一轮步长变成 lucky[1]=2。接着从 2 号继续走两步,最终 2 号出局,只剩下 0 号。

题解

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

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

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

全部评论

相关推荐

昨天 11:25
门头沟学院 Java
ZlzZlz:3个题我就a了0.25,真tm不会啊,如坐针毡
点赞 评论 收藏
分享
点赞 评论 收藏
分享
评论
1
1
分享

创作者周榜

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