美团秋招笔试真题 - 放它一马 & 信号模拟

放他一马

题目描述

小美会按照编号从小到大的顺序依次遇到 n 只怪物(编号为 1 ~ n),怪物 i(1 ≤ i ≤ n) 的生命为 ai。对于每只怪物,小美都可以选择放走 Ta 或者击败 Ta。如果放走怪物,小美将获得 i 点经验值。如果击败怪物,小美将获得 ai 点经验值,同时将额外获得 (x mod 10) × ai 点经验值,x 为击败怪物数量(包括这一个怪物)。求小美最多可以从这 n 个怪物中获得的经验值。

输入

第一行输入一个整数 n(1 ≤ n ≤ 2 × 105) 表示怪物数。第二行输入 n 个整数 ai(1 ≤ ai ≤ 109) 表示怪物的生命。

输出

输出一个整数表示小美可以获得最高的经验值。

示例1

输入

复制代码
3
5 3 2

输出

复制代码
27

思路和代码

实现:动态规划

  1. 设定f[i][j]表示为前i只怪物,击败j只怪兽(j mod 10)能够获得的最大经验。

  2. 状态转移方程:f[i][j] = max(f[i-1][j] + i, f[i-1][(j-1 + 10) %10] + i *(num+1)),分别表示放走和击败,取其中较大值。

  3. 最终答案就是f[n][j], j取值范围[1,9]中最大值。

优化/注意点:考虑到上述转移方程只和前一轮相关,可以使用一维滚动数组进行压缩。

CPP 复制代码
import sys

def main():
    n = int(sys.stdin.readline().strip())
    a = list(map(int, sys.stdin.readline().split()))

    dp = [0] * 10
    dp_last = [0] * 10

    for i in range(1, n + 1):
        num = a[i - 1]
        for j in range(10):
            if j <= i:
                dp[j] = max(dp_last[j] + i,
                            dp_last[(j - 1 + 10) % 10] + j * num + num)
        dp_last = dp[:]

    res = max(dp_last)
    print(res)


if __name__ == "__main__":
    main()

信号模拟

题目描述

如下所示,有 (2 × n) 个仪器,中间的方块是仪器的主体,每个仪器可以充当接收器或者信号源;主体的左右两侧是两个接线点。现在,我们将左端 (2 × n) 个接线点随机分成 (n) 组,每组各含两个点,并将右端 (2 × n) 个接线点同样随机分成 (n) 组。然后将每组的两个接线点用导线连接。这样一来,我们就得到了一组封闭的信号线路。具体而言:

  • 信号从任一信号源 (i) 出发,通过右侧接线点;
  • 随后,信号通过与右侧接线点连接的导线到达另外一个仪器的左侧接线点,再经过仪器主体到达右侧接线点;
  • 此时,如果这个仪器是接收器,那么就视为接收到了信号(注意,接收到信号不会影响信号继续往后传递)。
  • 这个过程持续进行,最终会形成若干个独立的循环。

现在,记 (x) 表示在所有接收器均能接收到信号的前提下,(2 × n) 个仪器中作为信号源的最少数数量。求解 (x) 的方差。可以证明答案可以表示为一个不可约分数 (p/q),为了避免精度问题,请直接输出整数 (p × q-1 mod M) 作为答案,其中 (M = 998244353),q-1 是满足 q × q-1 ≡ 1 (mod M) 的整数。

输入

每个测试文件均包含多组测试数据。第一行输入一个整数 (T (1 <= T <= 10^4)) 代表数据组数,每组测试数据描述如下:在一行上输入一个整数 (n (1 <= n <= 10^6)) 代表仪器的数量。

输出

对于每组测试数据,新起一行输出一个整数,表示 (x) 的方差对 (M = 998244353) 取模后的结果。

提示

本题中如果需要使用除法的取模,即计算(p * q ^-1 mod M)时,(q^-1)需要使用公式((q^ M-2 MOD M))得到,

示例1

输入

复制代码
3
1
2
3

输出

复制代码
0
887328314
168592380

思路和代码

实现:乘法逆元 + 前缀和

  1. 设X为最少信号源数,E(x)为期望,E(x ^2)为二次期望,D(X) = E(x ^2) - E(x) ^2
  2. inv[k]为k的逆元,设置total1total1[n]1 - n1/2(j -1)的前缀和,设置total2total2[n]1 - n(1/2(j -1))^2的前缀和,
  3. 初始化预计算逆元和前缀和
  4. 对于输入为n的情况下结果即为total[n] - total2[n]
CPP 复制代码
#include<iostream>
#include<vector>
#include<string>
#include <utility> 
#include<algorithm> 
#include<cmath>
#include<map>
#include<stack>
#include<unordered_map>
using namespace std;
typedef long long LL ;

const int MOD = 998244353, N = 1e6 + 10;
// inv[i] 等于 i在MOD下的逆元
LL inv[2 * N];
// total1[i] 表示 1/(2i-1)的前缀和
LL total1[N];
// total2[i]表示(1/(2i-1))^2的前缀和
LL total2[N];




int main() {
    ios::sync_with_stdio(false); 
    cin.tie(nullptr); 
    int T;
    cin >> T;
    int input[T],maxv = 0;
    for (int i = 0; i < T;i++) {
        cin >> input[i];
        maxv = max(maxv, input[i]);
    }
    
    // 最大逆元下标
    int maxv2 = 2 * maxv;
    inv[1] = 1;
    // 递推
    for (int i = 2; i <= maxv2; i++) {
        inv[i] = (MOD - MOD / i) * inv[MOD % i] % MOD;
    }
    
    // 预处理前缀和
    total1[0] = 0;
    total2[0] = 0;
    // 预计算前缀和
    for (int j = 1; j <= maxv; j++) {
        LL x = inv[2 * j - 1];
        total1[j] = (total1[j - 1] + x) % MOD;
        total2[j] =  (total2[j-1] + x * x % MOD) %MOD;
    }
    
    for (int i = 0; i < T; i++) {
        int n = input[i];
        // 方差公式 D(X) = sum1[n] = sum2[2]
        long long res = (total1[n] - total2[n] + MOD) % MOD;
        cout << res << endl;
    }
    return 0;
}
相关推荐
一起养小猫2 小时前
LeetCode100天Day13-移除元素与多数元素
java·算法·leetcode
ACERT3333 小时前
10.吴恩达机器学习——无监督学习01聚类与异常检测算法
python·算法·机器学习
诗词在线3 小时前
从算法重构到场景复用:古诗词数字化的技术破局与落地实践
python·算法·重构
不穿格子的程序员3 小时前
从零开始写算法——二叉树篇7:从前序与中序遍历序列构造二叉树 + 二叉树的最近公共祖先
数据结构·算法
hetao17338373 小时前
2026-01-12~01-13 hetao1733837 的刷题笔记
c++·笔记·算法
qq_433554543 小时前
C++ 图论算法:强连通分量
c++·算法·图论
YuTaoShao3 小时前
【LeetCode 每日一题】2943. 最大化网格图中正方形空洞的面积——(解法二)哈希集合
算法·leetcode·哈希算法
开开心心就好3 小时前
内存清理工具显示内存,优化释放自动清理
java·linux·开发语言·网络·数据结构·算法·电脑
蚊子码农3 小时前
算法题解记录-131分割回文串
算法