美团2024春招第一场笔试

P1

题目

小美的平衡矩阵

小美拿到了一个n*nn∗n的矩阵,其中每个元素是 0 或者 1。
小美认为一个矩形区域是完美的,当且仅当该区域内 0 的数量恰好等于 1 的数量。
现在,小美希望你回答有多少个i*ii∗i的完美矩形区域。你需要回答1≤i≤n的所有答案。

n <= 200

思路

此题数据量较少,简单思路为遍历所有点,取到所有面,统计面中0和1的数量即可。但时间复杂度过高,遍历所有点为O(n^2)再遍历对角点为O(n^3)再统计数量为O(n^5)。数据量虽小,这样也明显不适合。

因为要统计范围内总数,常见的思路为前缀和数组,本题最优解应建立二维前缀和数组

时间复杂度应为:

建立数组:O(n^2);

遍历点:O(n^2);

遍历对角点:O(n);

得出结果为:O(1);

总时间复杂度为O(n^3);

答题时直接用一维数组记录列的前缀和即通过,时间复杂度为O(n^4)

代码

import java.util.Scanner;
 
// 注意类名必须为 Main, 不要有任何 package xxx 信息
public class Main {
    public static void main(String[] args) {
        Scanner sc = new Scanner(System.in);
        int n = Integer.parseInt(sc.nextLine());
        int[][] grap = new int[n][n];
        int[][] preOne = new int[n][n];
        for (int i = 0; i < n; i++) {
            char[] s = sc.nextLine().toCharArray();
            for (int j = 0; j < n; j++) {
                grap[i][j] = s[j] == '0' ? 0 : 1;
                if (i == 0) {
                    preOne[i][j] = grap[i][j] == 1 ? 1 : 0;
                } else {
                    preOne[i][j] = preOne[i - 1][j];
                    preOne[i][j] += grap[i][j] == 1 ? 1 : 0;
                }
 
            }
        }
        int[] ans = new int[n + 1];
        for (int i = 0; i < n - 1; i++) {
            for (int j = 0; j < n - 1; j++) {
                // pointX (i,j)
 
                for (int m = i + 1, k = j + 1; m < n && k < n; m++, k++) {
                    //pointY(m,k)
                    int c = m - i + 1;
                    int sum = (int) Math.pow(m - i + 1, 2);
                    int countOne = 0;
                    for (int l = j; l <= k; l++) {
                        countOne += preOne[m][l] - (i - 1 >= 0 ? preOne[i - 1][l] : 0);
                    }
                    if (sum - countOne == countOne ) {
                        ans[c]++;
                    }
 
                }
 
            }
        }
        for(int i = 1;i <= n;i++){
            System.out.println(ans[i]);
        }
    }
}

P2

题目

小美的数组询问

小美拿到了一个由正整数组成的数组,但其中有一些元素是未知的(用 0 来表示)。
现在小美想知道,如果那些未知的元素在区间[l,r][l,r]范围内随机取值的话,数组所有元素之和的最小值和最大值分别是多少?
共有qq次询问。

输入描述:

第一行两个正整数nq代表数组长度与询问次数

接下来一行为数组

加下来q行为询问

示例1

输入例子:

3 2
1 0 3
1 2
4 4

输出例子:

5 6
8 8

例子说明:

只有第二个元素是未知的。
第一次询问,数组最小的和是 1+1+3=5,最大的和是 1+2+3=6。
第二次询问,显然数组的元素和必然为 8。

思路

只要注意数据范围即可,记录下数组和以及0的个数,用最大最小值简单运算即可。要着重注意数据范围,可能卡数据。

代码

import java.util.Scanner;

public class Main {
    public static void main(String[] args) {
        Scanner scanner = new Scanner(System.in);
        int n = scanner.nextInt();
        int q = scanner.nextInt();
        long tmp, zeroCnt = 0, nonZeroSum = 0;
        for (int i = 0; i < n; i++) {
            tmp = scanner.nextLong();
            if (tmp == 0) {
                zeroCnt++;
            } else {
                nonZeroSum += tmp;
            }
        }
        while (q-- > 0) {
            long l = scanner.nextLong();
            long r = scanner.nextLong();
            System.out.println((nonZeroSum + zeroCnt * l) + " " + (nonZeroSum + zeroCnt * r));
        }
    }
}

P3

题目

MT 是美团的缩写,因此小美很喜欢这两个字母。
现在小美拿到了一个仅由大写字母组成字符串,她可以最多操作k次,每次可以修改任意一个字符。小美想知道,操作结束后最多共有多少个'M'和'T'字符?

思路

此题思路清晰,遍历字符串统计M,T次数即可,最后+k和数组长度取最小值。

代码

import java.util.Scanner;

// 注意类名必须为 Main, 不要有任何 package xxx 信息
public class Main {
    public static void main(String[] args) {
        Scanner sc = new Scanner(System.in);
        String[] nk = sc.nextLine().split(" ");
        int n = Integer.parseInt(nk[0]);
        int k = Integer.parseInt(nk[1]);
        char[] s = sc.nextLine().toCharArray();
        int res = 0;
        for(int i = 0;i < n;i++){
            if(s[i] == 'M' || s[i] == 'T'){
                res++;
            }
        }
        res += k;
        System.out.println(res > n ? n : res);
    }
}

P4

题目

小美的朋友关系

小美认为,在人际交往中,但是随着时间的流逝,朋友的关系也是会慢慢变淡的,最终朋友关系就淡忘了。
现在初始有一些朋友关系,存在一些事件会导致两个人淡忘了他们的朋友关系。小美想知道某一时刻中,某两人是否可以通过朋友介绍互相认识?
事件共有 2 种:
1 u v:代表编号 u 的人和编号 v 的人淡忘了他们的朋友关系。
2 u v:代表小美查询编号 u 的人和编号 v 的人是否能通过朋友介绍互相认识。

注:介绍可以有多层,比如 2 号把 1 号介绍给 3 号,然后 3 号再把 1 号介绍给 4 号,这样 1 号和 4 号就认识了。

输入描述:
第一行输入三个正整数n,m,q,代表总人数,初始的朋友关系数量,发生的事件数量。
接下来的m行,每行输入两个正整数u,v,代表初始编号u的人和编号v的人是朋友关系。
接下来的q行,每行输入三个正整数op,u,v,含义如题目描述所述。
1\leq n \leq 10^9
1\leq m,q \leq 10^5
1\leq u,v \leq n
1\leq op \leq 2
保证至少存在一次查询操作。
输出描述:
对于每次 2 号操作,输出一行字符串代表查询的答案。如果编号 u 的人和编号 v 的人能通过朋友介绍互相认识,则输出"Yes"。否则输出"No"。
示例1
输入例子:
5 3 5
1 2
2 3
4 5
1 1 5
2 1 3
2 1 4
1 1 2
2 1 3
输出例子:
Yes
No
No
例子说明:
第一次事件,1 号和 5 号本来就不是朋友,所以无事发生。
第二次事件是询问,1 号和 3 号可以通过 2 号的介绍认识。
第三次事件是询问,显然 1 号和 4 号无法互相认识。
第四次事件,1 号和 2 号淡忘了。
第五次事件,此时 1 号无法再经过 2 号和 3 号互相认识了。

思路

很自然可以想到用并查集可解,但并查集并不能删除边,也无法还原他们本身的关系。所以此题应该逆向思维,正难则反。根据关系生成好并查集,再逆序判断,遇到2时间查询,1时间合并即可。

最终时间复杂度还是超了,据分析时间复杂度应该是O(n) + O(q)但n已超过10^8次方,也没想到更低的方法。

代码

import java.util.*;

// 注意类名必须为 Main, 不要有任何 package xxx 信息
public class Main {
     public static class Union{
         int[] parent;
         int n;
        public Union(int n){
            this.n = n;
            parent = new int[n];
            for(int i = 0;i < n;i++){
                parent[i] = i;
            }
        }
        public  int find(int node){
            int res = parent[node];
            int[] stack = new int[n];
            int size = 0;
            while(parent[res] != res){
                stack[size++] = res;
                res = parent[res];
            }
            while(--size >= 0){
                parent[stack[size]] = res;
            }
            return res;
        }
        public  void union(int a,int b){
            int parentA = find(a);
            int parentB = find(b);
            if(parentA != parentB){
                parent[parentA] = parentB;
            }
        }
        public  boolean isUnion(int a,int b){
            return find(a) == find(b);
        }
    }
    public static void main(String[] args) {
        Scanner sc = new Scanner(System.in);
        int n = sc.nextInt();
        int m = sc.nextInt();
        int q = sc.nextInt();
        Union union = new Union(n);
        HashSet<String> set = new HashSet<>();
        while(m-- > 0){
            int u = sc.nextInt() - 1;
            int v = sc.nextInt() - 1;
            String s = (u <= v )? u + " " + v : v + " " + u;
            set.add(s);
        }
        int[][] query = new int[q][3];
        for(int i = 0;i < q;i++){
            query[i][0] = sc.nextInt();
            query[i][1] = sc.nextInt() - 1;
            query[i][2] = sc.nextInt() - 1;
        }
        int co = 0;
        for(int i = 0;i < q;i++){
            if(query[i][0] == 1){
                int u = query[i][1];
                int v = query[i][2];
                String s = (u <= v )? u + " " + v : v + " " + u;
                if(set.contains(s)){
                    set.remove(s);
                }
                co++;
            }
        }
        boolean[] ans = new boolean[q - co];
        for(String s : set){
            int a = Integer.parseInt(s.split(" ")[0]);
            int b = Integer.parseInt(s.split(" ")[1]);
            union.union(a,b);
        }
        int index = ans.length;
        for(int i = q - 1;i >= 0;i--){
            switch(query[i][0]){
                case(1):
                    union.union(query[i][1],query[i][2]);
                    break;
                case(2):
                    ans[--index] = union.isUnion(query[i][1],query[i][2]);
                    break;
            }
        }
        for(int i = 0;i < ans.length;i++){
            System.out.println(ans[i] ? "Yes" : "No");
        }
    }
}import java.util.*;

// 注意类名必须为 Main, 不要有任何 package xxx 信息
public class Main {
     public static class Union{
         int[] parent;
         int n;
        public Union(int n){
            this.n = n;
            parent = new int[n];
            for(int i = 0;i < n;i++){
                parent[i] = i;
            }
        }
        public  int find(int node){
            int res = parent[node];
            int[] stack = new int[n];
            int size = 0;
            while(parent[res] != res){
                stack[size++] = res;
                res = parent[res];
            }
            while(--size >= 0){
                parent[stack[size]] = res;
            }
            return res;
        }
        public  void union(int a,int b){
            int parentA = find(a);
            int parentB = find(b);
            if(parentA != parentB){
                parent[parentA] = parentB;
            }
        }
        public  boolean isUnion(int a,int b){
            return find(a) == find(b);
        }
    }
    public static void main(String[] args) {
        Scanner sc = new Scanner(System.in);
        int n = sc.nextInt();
        int m = sc.nextInt();
        int q = sc.nextInt();
        Union union = new Union(n);
        HashSet<String> set = new HashSet<>();
        while(m-- > 0){
            int u = sc.nextInt() - 1;
            int v = sc.nextInt() - 1;
            String s = (u <= v )? u + " " + v : v + " " + u;
            set.add(s);
        }
        int[][] query = new int[q][3];
        for(int i = 0;i < q;i++){
            query[i][0] = sc.nextInt();
            query[i][1] = sc.nextInt() - 1;
            query[i][2] = sc.nextInt() - 1;
        }
        int co = 0;
        for(int i = 0;i < q;i++){
            if(query[i][0] == 1){
                int u = query[i][1];
                int v = query[i][2];
                String s = (u <= v )? u + " " + v : v + " " + u;
                if(set.contains(s)){
                    set.remove(s);
                }
                co++;
            }
        }
        boolean[] ans = new boolean[q - co];
        for(String s : set){
            int a = Integer.parseInt(s.split(" ")[0]);
            int b = Integer.parseInt(s.split(" ")[1]);
            union.union(a,b);
        }
        int index = ans.length;
        for(int i = q - 1;i >= 0;i--){
            switch(query[i][0]){
                case(1):
                    union.union(query[i][1],query[i][2]);
                    break;
                case(2):
                    ans[--index] = union.isUnion(query[i][1],query[i][2]);
                    break;
            }
        }
        for(int i = 0;i < ans.length;i++){
            System.out.println(ans[i] ? "Yes" : "No");
        }
    }
}

P5

题目

小美的区间删除

小美拿到了一个大小为nn的数组,她希望删除一个区间后,使得剩余所有元素的乘积末尾至少有kk个 0。小美想知道,一共有多少种不同的删除方案?

输入描述:
第一行输入两个正整数n,k。
第二行输入n个正整数a_i,代表小美拿到的数组。
1\leq n,k \leq 10^5
1\leq a_i \leq 10^9
输出描述:
一个整数,代表删除的方案数。
示例1
输入例子:
5 2
2 5 3 4 20
输出例子:
4
例子说明:
第一个方案,删除[3]。
第二个方案,删除[4]。
第三个方案,删除[3,4]。
第四个方案,删除[2]。

思路

遇到末尾0或10的倍数等问题,最先想到的就应该是2和5组成10,根据2和5的个数必定递增,可以建立单调性,有单调性的问题一般可用二分或滑动窗口解图,此题又是数组区间问题,对于区间问题首先想道的应为滑动窗口。

此题按滑动窗口统计2和5的个数最小值即可,减去窗口内的个数判断是否满足条件即可。滑动窗口亦有模板。

代码

import java.util.Scanner;

// 注意类名必须为 Main, 不要有任何 package xxx 信息
public class Main {
    public static void main(String[] args) {
        Scanner sc = new Scanner(System.in);
        int n = sc.nextInt();
        int k = sc.nextInt();
        int twos = 0;
        int fivs = 0;
        int[] tws = new int[n];
        int[] fis = new int[n];
        long num;
        for (int i = 0; i < n; i++) {
            num = sc.nextInt();
            while (num % 2 == 0) {
                twos++;
                tws[i]++;
                num /= 2;
            }
            while (num % 5 == 0) {
                fivs++;
                fis[i]++;
                num /= 5;
            }
        }
        //[r + 1] - [l]
        long ans = 0;
        for (int l = 0, r = 0; r < n; r++ ) {
            twos -= tws[r];
            fivs -= fis[r];
            while (l <= r && Math.min(twos, fivs) < k) {
                twos += tws[l]; fivs += fis[l];
                ++ l;
            }
            ans += r- l + 1;
        }
        System.out.println(ans);


    }
}

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值