【拼多多笔试题汇总】2024-03-24-拼多多春招笔试题

🍭 大家好这里是KK爱Coding ,一枚热爱算法的程序员

✨ 本系列打算持续跟新拼多多近期的春秋招笔试题汇总~

💻 ACM银牌🥈| 多次AK大厂笔试 | 编程一对一辅导

👏 感谢大家的订阅➕ 和 喜欢💗

01.小明与K小姐的游戏

问题描述

A 小镇上有个奇特的游戏。游戏开始时,A 先生准备了一排 n n n 个物品。K 小姐和卢小姐则轮流进行操作。

首先,K 小姐可以选择移除至多 x x x 个物品。接着,卢小姐也可以选择移除剩余物品中的至多 y y y 个。

游戏的目标是,K 小姐想尽量让剩余物品的价值之和最大化,而卢小姐则想将剩余物品的价值之和最小化。假设两人都采取最优策略,请问最后剩余物品的价值之和是多少?

输入格式

第一行包含五个正整数 n , m , k , x , y n, m, k, x, y n,m,k,x,y,分别表示物品的总数、K 小姐最多可移除的物品数、卢小姐最多可移除的物品数、以及每件物品的价值上限。

第二行共 n n n 个空格分开的正整数 A 1 , A 2 , . . . , A n A_1, A_2, ... , A_n A1,A2,...,An,表示每件物品的价值。

输出格式

输出一个整数,表示最终剩余物品的价值之和。

样例输入

3 1 1 1 1
4 3 2

样例输出

1

数据范围

  • 2 ≤ n ≤ 1 0 5 2 \leq n \leq 10^5 2n105
  • 0 ≤ m , d ≤ n 0 \leq m, d \leq n 0m,dn
  • 1 ≤ k ≤ 1 0 4 1 \leq k \leq 10^4 1k104
  • 1 ≤ a i ≤ 1 0 9 1 \leq a_i \leq 10^9 1ai109
  • 保证 ∑ n \sum{n} n 不超过 1 0 5 10^5 105

题解

本题可以转化为一个博弈问题。我们首先对物品价值进行降序排序,然后计算前缀和。

如果 K 小姐不移除任何物品,那么卢小姐为了最小化剩余物品价值之和,就需要将价值最大的 m m m 件物品的价值取负数。这时候最终价值之和为前 n − m n-m nm 件物品的价值之和减去剩余 m m m 件物品的价值之和。

如果 K 小姐移除了 d d d 件物品,那么卢小姐为了最小化剩余物品价值之和,也需要将剩余物品中价值最大的 m m m 件物品的价值取负数。这时候最终价值之和为前 n − d − m n-d-m ndm 件物品的价值之和减去剩余 m m m 件物品的价值之和。

因此,我们可以枚举 K 小姐移除的物品数量 d d d,对于每个 d d d,再枚举卢小姐取负的物品数量 m m m,计算出最终的价值之和,取其中的最大值即可。

时间复杂度为 O ( n ) O(n) O(n),空间复杂度为 O ( n ) O(n) O(n)

参考代码

  • Python
from typing import List

def maxValueAfterOperations(n: int, m: int, k: int, x: int, y: int, A: List[int]) -> int:
    A.sort(reverse=True)
    for i in range(1, len(A)):
        A[i] += A[i - 1]

    ans = A[-1]
    for d in range(x + 1):
        for rm in range(min(y, len(A) - d)):
            val = A[-1] - A[len(A) - d - rm - 1]
            sub = A[len(A) - d - 1] - (0 if d == 0 else A[d - 1])
            ans = max(ans, val - k * sub)

    return ans

if __name__ == '__main__':
    n, m, k, x, y = map(int, input().split())
    A = list(map(int, input().split()))
    print(maxValueAfterOperations(n, m, k, x, y, A))
  • Java
import java.util.*;

class Solution {
    public static long maxValueAfterOperations(int n, int m, int k, int x, int y, int[] A) {
        List<Long> vals = new ArrayList<>();
        for (int a : A) {
            vals.add((long) a);
        }
        Collections.sort(vals, Collections.reverseOrder());

        for (int i = 1; i < vals.size(); i++) {
            vals.set(i, vals.get(i) + vals.get(i - 1));
        }

        long ans = vals.get(vals.size() - 1);
        for (int d = 0; d <= x; d++) {
            for (int rm = 0; rm <= Math.min(y, vals.size() - d - 1); rm++) {
                long val = vals.get(vals.size() - 1);
                long sub = (d == 0 ? 0 : vals.get(d - 1));
                val -= vals.get(vals.size() - rm - d - 1) - sub;
                sub = vals.get(vals.size() - rm - 1) - sub;
                ans = Math.max(ans, val - k * sub);
            }
        }

        return ans;
    }

    public static void main(String[] args) {
        Scanner sc = new Scanner(System.in);
        int n = sc.nextInt(), m = sc.nextInt(), k = sc.nextInt(), x = sc.nextInt(), y = sc.nextInt();
        int[] A = new int[n];
        for (int i = 0; i < n; i++) {
            A[i] = sc.nextInt();
        }
        System.out.println(maxValueAfterOperations(n, m, k, x, y, A));
    }
}
  • Cpp
#include <bits/stdc++.h>
using namespace std;

class Solution {
public:
    long long maxValueAfterOperations(int n, int m, int k, int x, int y, vector<int>& A) {
        sort(A.begin(), A.end(), greater<int>());
        vector<long long> vals(A.begin(), A.end());
        for (int i = 1; i < n; i++) {
            vals[i] += vals[i - 1];
        }

        long long ans = vals.back();
        for (int d = 0; d <= x; d++) {
            for (int rm = 0; rm <= min(y, n - d - 1); rm++) {
                long long val = vals.back();
                long long sub = (d == 0 ? 0 : vals[d - 1]);
                val -= vals[n - rm - d - 1] - sub;
                sub = vals[n - rm - 1] - sub;
                ans = max(ans, val - k * sub);
            }
        }

        return ans;
    }
};

int main() {
    int n, m, k, x, y;
    cin >> n >> m >> k >> x >> y;
    vector<int> A(n);
    for (int i = 0; i < n; i++) {
        cin >> A[i];
    }
    Solution sol;
    cout << sol.maxValue
      AfterOperations(n, m, k, x, y, A) << endl;
    return 0;
}

02.LYA小姐的字符串拼接探奇

问题描述

LYA 小姐非常喜欢探索字符串的奇特性质。有一天,她想到了一种新的字符串生成方法。她有两个由 0 和 1 组成的字符串 A A A B B B,其中 A A A 的长度为 m m m B B B 的长度为 n n n 1 ≤ n ≤ m ≤ 5000 1 \le n \le m \le 5000 1nm5000)。她将从 A A A 中选取一个长度为 n n n 的子串,并对这个子串的每一个字符与 B B B 中对应位置的字符进行异或操作,生成一个新的字符串。

LYA 小姐认为,如果生成的字符串所有字符的异或结果为 0 0 0,那么这个字符串就是一个"合法"的字符串。她现在想知道,对于给定的 A A A B B B,一共有多少种不同的子串可以生成"合法"的字符串?需要注意的是,即使两个子串生成的字符串相同,只要子串本身不同,就应当被视为不同的子串。

输入格式

第一行包含两个正整数 m m m n n n,分别表示字符串 A A A B B B 的长度。
第二行是一个长度为 m m m 的字符串 A A A
第三行是一个长度为 n n n 的字符串 B B B

输出格式

输出一个整数,表示可以生成"合法"字符串的不同子串的个数。

样例输入

8 2
10100000
01

样例输出

2

数据范围

对于所有测试数据,都满足 1 ≤ n ≤ m ≤ 5000 1 \le n \le m \le 5000 1nm5000

题解

这个问题可以使用滑动窗口的思路来解决。我们首先计算出 B B B 字符串所有字符的异或结果 c n t B cntB cntB。然后遍历 A A A 字符串,维护一个长度为 n n n 的滑动窗口,用 c n t A cntA cntA 记录该窗口内所有字符的异或结果。如果 c n t A ⊕ c n t B = 0 cntA \oplus cntB = 0 cntAcntB=0,则说明该窗口对应的子串可以生成一个"合法"字符串,我们将其添加到一个集合中。由于集合的特性,相同的子串只会被添加一次。最后返回集合的大小即可。

需要注意的是,在滑动窗口的过程中,我们需要维护 c n t A cntA cntA 的值。当窗口向右滑动时,需要将最右边的字符的值异或到 c n t A cntA cntA 中;当窗口向左滑动时,需要将最左边的字符的值从 c n t A cntA cntA 中异或掉。

参考代码

  • Python
def soln(m, n, A, B):
    cntB = 0
    for b in B:
        cntB ^= int(b)
    
    cntA = 0
    window = []
    ans = set()
    
    for a in A:
        window.append(a)
        cntA ^= int(a)
        
        if len(window) > n:
            cntA ^= int(window.pop(0))
        
        if len(window) == n and (cntA ^ cntB) == 0:
            ans.add(''.join(window))
    
    return len(ans)
  • Java
import java.util.*;

public class Solution {
    public static int soln(int m, int n, String A, String B) {
        int cntB = 0;
        for (char b : B.toCharArray()) {
            cntB ^= (b - '0');
        }
        
        int cntA = 0;
        Deque<Character> window = new LinkedList<>();
        Set<String> ans = new HashSet<>();
        
        for (char a : A.toCharArray()) {
            window.addLast(a);
            cntA ^= (a - '0');
            
            if (window.size() > n) {
                cntA ^= (window.removeFirst() - '0');
            }
            
            if (window.size() == n && (cntA ^ cntB) == 0) {
                ans.add(window.stream().map(Object::toString).collect(Collectors.joining()));
            }
        }
        
        return ans.size();
    }
}
  • Cpp
#include <iostream>
#include <string>
#include <unordered_set>
#include <deque>
using namespace std;

int soln(int m, int n, string A, string B) {
    int cntB = 0;
    for (char b : B) {
        cntB ^= (b - '0');
    }
    
    int cntA = 0;
    deque<char> window;
    unordered_set<string> ans;
    
    for (char a : A) {
        window.push_back(a);
        cntA ^= (a - '0');
        
        if (window.size() > n) {
            cntA ^= (window.front() - '0');
            window.pop_front();
        }
        
        if (window.size() == n && (cntA ^ cntB) == 0) {
            ans.insert(string(window.begin(), window.end()));
        }
    }
    
    return ans.size();
}

03.热气球俱乐部的路径探索

问题描述

LYA 小姐是一家热气球俱乐部的会员。这个俱乐部有 n n n 个起飞点,通过 m m m 条单向航线相连。热气球只能沿着单向航线飞行,不能返回起飞点。也就是说, n n n 个起飞点组成了一张有向无环图。

如果从某个起飞点 u u u 出发,可以到达所有其他起飞点,或者所有其他起飞点都可以到达 u u u,那么就认为 u u u 是一个"超级起飞点"。LYA 小姐想知道,在这个俱乐部中一共有多少个"超级起飞点"?

输入格式

第一行包含两个正整数 n n n m m m,分别表示起飞点的个数和单向航线的条数,其中 2 ≤ n ≤ 3 × 1 0 5 2 \le n \le 3 \times 10^5 2n3×105, 1 ≤ m < 3 × 1 0 5 1 \le m < 3 \times 10^5 1m<3×105

接下来的 m m m 行,每行包含两个正整数 u u u v v v ( 1 ≤ u , v ≤ n , u ≠ v 1 \le u, v \le n, u \neq v 1u,vn,u=v),表示存在一条从起飞点 u u u 到起飞点 v v v 的单向航线。

输出格式

输出一个整数,表示"超级起飞点"的个数。

样例输入

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

样例输出

2

数据范围

对于所有测试数据,都满足 2 ≤ n ≤ 3 × 1 0 5 , 1 ≤ m < 3 × 1 0 5 2 \le n \le 3 \times 10^5,1 \le m < 3 \times 10^5 2n3×105,1m<3×105

题解

要解决这个问题,我们可以先对有向图进行拓扑排序,得到一个点的拓扑序。然后计算每个点的最早开始时间和最晚开始时间。超级起飞点必须满足最早开始时间和最晚开始时间相等。

我们按照最晚开始时间从大到小、最早开始时间从小到大的顺序遍历所有点。对于每个点,如果它满足最早开始时间等于最晚开始时间,那么它就是一个候选的超级起飞点。但是,如果存在多个点的最早开始时间和最晚开始时间都相等,那么我们只计算第一个点,因为后面的点都在第一个点的支配范围内。

另外,如果一个点的最早开始时间等于某个已经计算过的超级起飞点的最早开始时间,那么这个点也不是超级起飞点,因为它在那个超级起飞点的支配范围内。

通过这种方式,我们可以正确地计算出所有的超级起飞点。

参考代码

  • Python
from collections import deque

def soln(n, m, edges):
    adj = [[] for _ in range(n)]
    for u, v in edges:
        u -= 1
        v -= 1
        adj[u].append(v)
    
    # 拓扑排序
    ind = [0] * n
    for u in range(n):
        for v in adj[u]:
            ind[v] += 1
    q = deque([u for u in range(n) if ind[u] == 0])
    order = []
    while q:
        u = q.popleft()
        order.append(u)
        for v in adj[u]:
            ind[v] -= 1
            if ind[v] == 0:
                q.append(v)
    
    # 计算最早开始时间和最晚开始时间
    min_time = [0] * n
    max_time = [0] * n
    for u in order:
        for v in adj[u]:
            min_time[v] = max(min_time[v], min_time[u] + 1)
            max_time[v] = max(max_time[v], max_time[u] + 1)
    
    # 找出超级起飞点
    super_points = []
    min_start = float('inf')
    for u in sorted(range(n), key=lambda x: (-max_time[x], min_time[x])):
        if min_time[u] == max_time[u]:
            if min_time[u] >= min_start:
                super_points.append(u)
                min_start = min_time[u]
            else:
                continue
        else:
            min_start = min(min_start, min_time[u])
    
    return len(super_points)

04.K小姐的字符串操作

问题描述

K小姐有一串由小写字母组成的字符串,她可以对这个字符串进行多次操作。每次操作,她都可以删除字符串中的一个回文子串(包括长度为 1 1 1 的单个字母)。删除后,剩余的字符串部分会自动拼接在一起。K小姐想知道,她最少需要进行多少次操作,才能将整个字符串删除干净。

输入格式

第一行包含一个正整数 T   ( 1 ≤ T ≤ 20 ) T\ (1 \le T \le 20) T (1T20),表示测试用例的数目。

每个测试用例占一行,给出一个仅由小写字母组成的字符串,字符串长度 n   ( 1 ≤ n ≤ 500 ) n\ (1 \le n \le 500) n (1n500),并且所有字符串的总长度不超过 3000 3000 3000

输出格式

对于每个测试用例,输出一个整数,表示K小姐最少需要进行多少次操作,才能将给定的字符串删除干净。

样例输入

3
mwapd
tvuvv
yxxml

样例输出

5
3
4

数据范围

对于所有测试用例,满足:

  • 1 ≤ T ≤ 20 1 \le T \le 20 1T20
  • 1 ≤ n ≤ 500 1 \le n \le 500 1n500
  • ∑ n ≤ 3000 \sum n \le 3000 n3000

题解

我们可以使用动态规划的方法来解决这个问题。定义 d p [ i ] [ j ] dp[i][j] dp[i][j] 表示将子串 s [ i . . j ] s[i..j] s[i..j] 删除需要的最小操作次数。

初始时,对于任意长度为 1 1 1 的子串 s [ i . . i ] s[i..i] s[i..i],我们有 d p [ i ] [ i ] = 1 dp[i][i] = 1 dp[i][i]=1

对于长度大于 1 1 1 的子串 s [ i . . j ] s[i..j] s[i..j],我们分两种情况讨论:

  1. 如果 s [ i ] = s [ j ] s[i] = s[j] s[i]=s[j],那么 s [ i . . j ] s[i..j] s[i..j] 就是一个回文串,我们可以一次性删除它,因此 d p [ i ] [ j ] = d p [ i + 1 ] [ j − 1 ] dp[i][j] = dp[i+1][j-1] dp[i][j]=dp[i+1][j1]

  2. 如果 s [ i ] ≠ s [ j ] s[i] \neq s[j] s[i]=s[j],那么我们需要枚举一个分割点 k k k,分别删除 s [ i . . k ] s[i..k] s[i..k] s [ k + 1.. j ] s[k+1..j] s[k+1..j],最后取最小值,即 d p [ i ] [ j ] = min ⁡ i ≤ k < j { d p [ i ] [ k ] + d p [ k + 1 ] [ j ] } dp[i][j] = \min\limits_{i \le k < j}\{dp[i][k] + dp[k+1][j]\} dp[i][j]=ik<jmin{dp[i][k]+dp[k+1][j]}

最终的答案就是 d p [ 0 ] [ n − 1 ] dp[0][n-1] dp[0][n1],表示删除整个字符串 s s s 需要的最小操作次数。

参考代码

  • Python
def soln(s):
    n = len(s)
    dp = [[float('inf')] * n for _ in range(n)]
    for i in range(n):
        dp[i][i] = 1
    for l in range(2, n + 1):
        for i in range(n - l + 1):
            j = i + l - 1
            if s[i] == s[j]:
                dp[i][j] = dp[i + 1][j - 1]
            else:
                for k in range(i, j):
                    dp[i][j] = min(dp[i][j], dp[i][k] + dp[k + 1][j])
    return dp[0][n - 1]

t = int(input())
for _ in range(t):
    s = input()
    print(soln(s))
  • Java
import java.util.*;

public class Main {
    public static int soln(String s) {
        int n = s.length();
        int[][] dp = new int[n][n];
        for (int i = 0; i < n; i++) {
            Arrays.fill(dp[i], Integer.MAX_VALUE);
        }
        for (int i = 0; i < n; i++) {
            dp[i][i] = 1;
        }
        for (int l = 2; l <= n; l++) {
            for (int i = 0; i <= n - l; i++) {
                int j = i + l - 1;
                if (s.charAt(i) == s.charAt(j)) {
                    dp[i][j] = dp[i + 1][j - 1];
                } else {
                    for (int k = i; k < j; k++) {
                        dp[i][j] = Math.min(dp[i][j], dp[i][k] + dp[k + 1][j]);
                    }
                }
            }
        }
        return dp[0][n - 1];
    }

    public static void main(String[] args) {
        Scanner sc = new Scanner(System.in);
        int t = sc.nextInt();
        while (t-- > 0) {
            String s = sc.next();
            System.out.println(soln(s));
        }
    }
}
  • Cpp
#include <bits/stdc++.h>
using namespace std;

int soln(string s) {
    int n = s.size();
    vector<vector<int>> dp(n, vector<int>(n, INT_MAX));
    for (int i = 0; i < n; i++) {
        dp[i][i] = 1;
    }
    for (int l = 2; l <= n; l++) {
        for (int i = 0; i <= n - l; i++) {
            int j = i + l - 1;
            if (s[i] == s[j]) {
                dp[i][j] = dp[i + 1][j - 1];
            } else {
                for (int k = i; k < j; k++) {
                    dp[i][j] = min(dp[i][j], dp[i][k] + dp[k + 1][j]);
                }
            }
        }
    }
    return dp[0][n - 1];
}

int main() {
    int t;
    cin >> t;
    while (t--) {
        string s;
        cin >> s;
        cout << soln(s) << endl;
    }
    return 0;
}

写在最后

📧 KK这边最近正在收集近一年互联网各厂的笔试题汇总,如果有需要的小伙伴可以关注后私信一下 KK领取~

在这里插入图片描述

  • 49
    点赞
  • 38
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
2023年3月11日,美团春季招聘笔试中共包含五道编程题目。以下是对每道题目的简要说明: 1. 题目一:这道题目要求解决一个数字统计的问题。可能涉及到的知识点包括数据结构、循环和条件判断等。解决问题的思路可能是使用字典等数据结构来保存统计结果,并使用循环逐个读取输入数据并进行统计。 2. 题目二:这道题目可能是一个字符串处理的问题。需要使用字符串的方法进行操作,如提取、接、查找和替换等。可能的解决思路包括使用正则表达式、切片和遍历等。 3. 题目三:这道题目可能涉及到算法和数据结构的知识。可能是一道涉及到数组、链表、树等数据结构的问题。解决思路可能包括遍历、递归、搜索和排序等。 4. 题目四:这道题目可能是一个动态规划的问题。需要根据给定的条件和规则,通过动态规划的方式求解问题。解决思路包括定义状态和转移方程,使用递推或记忆化搜索进行求解。 5. 题目五:这道题目可能是一个图论或网络问题。需要根据给定的图或网络结构,解决一个相关的问题。可能涉及到广度优先搜索、深度优先搜索、最短路径等知识。解决思路可能包括使用图或网络的相关算法进行求解。 以上只是对这五道编程题目的一些可能情况进行的简要描述,具体的题目内容可能会有所不同。希望这些信息能对你有所帮助!

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值