美团笔试

一共五个题,四个编程,一个附加题写测试用例的。我的第四题打车题只过了9%,剩下的都A了,下面是题干和代码

小美的序列检查

小美给小团一个n个数字构成的数字序列,问小团能不能经过重新排列后形成1到n的排列。

举例:

小美给小团[2, 1, 3],则可以经过重新排列后构成[1, 2, 3],这是可行的。

小美给小团[4, 4, 1, 3],则无法经过重新排列后构成[1, 2, 3, 4],这是不可行的。

为了防止小团靠运气碰对答案,小美会进行多组询问。

输入描述
第一行是一个数T,表示有T组数据。

对于每组数据:

第一行一个数字n表示小美给出的序列由n个数字构成。

接下来一行n个空格隔开的正整数。

输出描述
对于每组数据,如果可以重新排列后得到1到n的排列,回答一行Yes,如果不可以,回答No

注意大小写

思路

暴力

import java.io.*;
import java.util.*;

public class Main {
    public static void main(String[] args) throws IOException {
        BufferedReader reader = new BufferedReader(new InputStreamReader(System.in));
        int T = Integer.parseInt(reader.readLine());
        for(int t = 0; t < T; t++){
            int n = Integer.parseInt(reader.readLine());
            String[] item = reader.readLine().split(" ");
            int[] arr = new int[n];
            for(int i = 0; i < n; i++)arr[i] = Integer.parseInt(item[i]);
            Arrays.sort(arr);
            if (fun(arr,n)){
                System.out.println("Yes");
            }else{
                System.out.println("No");
            }
        }
    }
    private static boolean fun(int[] arr, int n) {
        Arrays.sort(arr);
        if(arr[0] != 1)return false;
        for (int i = 1; i < n; i++){
            if (arr[i] != arr[i - 1] + 1)return false;
        }
        return true;
    }
}

小美的回文串构建

小美现在有一个字符串,小美现在想知道能不能通过在字符串的尾端增加若干字符使得整个字符串变成一个回文串。

回文串的定义:若一个字符串,对他正序遍历和倒序遍历得到的结果是完全一致的,就称它是一个回文串。例如 abcba 就是一个回文串,因为无论正序还是倒序都是一样的。

对于字符串 abaaca,显然在该字符串末尾继续补上三个字符 aba 就可以构成 abaacaaba,就可以把原字符串变成回文串。换句话说,最少补上三个字符。

你的任务就是找到使得原来的字符串变成回文串所需要的最少字符数量。

本题数据保证没有空串,因此不考虑空串是否为回文串。

保证输入的字符串仅包含小写字母。

输入描述
一行一个字符串,代表小美交给你的字符串。

输出描述
一行一个整数,表示将小美给出的字符串变成回文字符串所需要添补的最少字符数量。

样例输入
abaaca
样例输出
3

输入样例2

aba

输出样例2

0

思路

一开始看到回文串以为是动态规划,仔细看了看发现根本不是,题目其实在找从最右边开始的最长回文串是多长,找到这个长度后直接用字符串长度减去最长回文串长度就是答案了,这样看的话这个最长回文串的最后边的字符其实已经顶死了,就是字符串最后一个字符!

我的思路是先遍历一遍字符串找到与最后一个字符相同字符的位置,再用贪心算法,从左往右依次判断两个相同字符间形成的字符串是不是回文串,是的话直接返回就行了,最差的情况就是只剩最后一个字符是回文串返回的是n-1。

import java.util.*;
import java.io.*;

public class Main {
    public static void main(String[] args) throws IOException {
        BufferedReader reader = new BufferedReader(new InputStreamReader(System.in));
        String s = reader.readLine();
        int n = s.length();
        int ret = n - 1;
        List<Integer> list = new ArrayList<>();
        char last = s.charAt(n - 1);
        for (int i = 0; i < n - 1; i++){
            if(s.charAt(i) == last)list.add(i);
        }
        for (int idx : list){
            int left = idx, right = n - 1;
            while (left <= right){
                if (s.charAt(left) == s.charAt(right)){
                    left++;
                    right--;
                }else break;
            }
            if (left > right){
                ret = n - (n - idx);
                break;
            }
        }
        System.out.println(ret);
    }
}

小美的机器人

小美在数轴上放置了若干个机器人,这些机器人每到整数时刻,就会检查是否和其他机器人重合。如果重合,它就会原地爆炸。

这些机器人的移动速度均为 1 。举例来说,如果一个机器人初始位于点3,然后它的方向是向右的,则时刻1会位于点4,时刻2会位于点5。

小美现在给小团这样一个任务:小美将给出所有机器人的初始位置和初始朝向。小团的任务是判断每个机器人的爆炸时刻。当然,如果有一些机器人永远不会爆炸,则输出-1。

小团向你求助。你能帮帮小团吗?

注意事项1:一个机器人爆炸了之后,就不会再继续存在在这个数轴上。

举例来说,如果有三个机器人,一个位于位置0,向右,一个位于位置2,向右,一个位于位置4,向左。则时刻1的时候,后两个机器人会在位置3相遇并发生爆炸,此后第一个机器人和第三个机器人不会在时刻2继续爆炸(因为此时已经不存在第三个机器人了)

注意事项2:请注意,只有整数时刻机器人才会检查重合。

举例来说,如果有两个机器人,一个位于位置1,向右,一个位于位置2,向左,则它们并不会在整数时刻重合。因此它们两个不存在相遇爆炸。

注意事项3:保证机器人初始时刻不会重叠。换句话说,不存在在时刻0就立刻爆炸的机器人。

输入描述
第一行一个正整数 n 表示有 n 个机器人。

接下来 n 行,每行一个正整数和一个字符,以空格分隔。正整数代表机器人的坐标,字符为大写字母 L 和 R 的其中一个,分别表示机器人向左运动 和 向右运动。

输出描述
输出 n 行,每行一个数字,对应表示每个机器人的答案:

若该机器人会爆炸,输出爆炸时间;若该机器人不会爆炸,输出-1。

样例输入
10
94 R
74 L
90 L
75 R
37 R
99 R
62 R
4 L
92 L
44 R
样例输出
-1
6
23
-1
-1
-1
6
-1
-1
23

提示
数据范围和说明

对于所有数据都保证机器人的坐标处于[1, 1e9]的正整数范围内。

其中,对于30%的数据,保证机器人数量 n <= 10

对于100%的数据,保证机器人数量 n <= 1,000

思路

一开始处理输入数据时是把所有机器人的初始位置放到一个数组中,方向放到另一个数组中,然后想模拟每一时刻机器人的运动,但这个思路存在是个很致命的问题,就是没法判断什么时候该停止,因为不是每一个时刻都必须有机器人爆炸,所有还是要从头来处理数据。

机器人爆炸的一个必须条件是一个机器人向左而另一个机器人向右,这样才有可能相遇,所有我把机器人按左右分为两组放到两个hashmap中,key是机器人的index,value是机器人的初始位置。

每次循环分别遍历两个map找到向左的机器人的初始位置大于向右并且相减是偶数(只有相减是偶数才能相遇)的相邻最小的多组机器人处理,用一个jump数组处理已经爆炸或者没有可能爆炸的机器人。

import java.io.*;
import java.util.*;

public class Main {
    public static void main(String[] args) throws IOException {
        BufferedReader reader = new BufferedReader(new InputStreamReader(System.in));
        int n = Integer.parseInt(reader.readLine());
        int[] ret = new int[n];
        Arrays.fill(ret, -1);
        HashMap<Integer,Integer> leftMap = new HashMap<>();
        HashMap<Integer,Integer> rightMap = new HashMap<>();
        for (int i = 0; i < n; i++){
            String[] s = reader.readLine().split(" ");
            if (Objects.equals(s[1], "L")){
                leftMap.put(i,Integer.parseInt(s[0]));
            }else{
                rightMap.put(i,Integer.parseInt(s[0]));
            }
        }
        boolean[] jump = new boolean[n];
        while (!leftMap.isEmpty() && !rightMap.isEmpty()){
            int minTime = Integer.MAX_VALUE;
            List<Integer> list = new ArrayList<>();
            for (int key1 : leftMap.keySet()){
                if (jump[key1])continue;
                int pos1 = leftMap.get(key1);
                boolean flag = false;
                for (int key2 : rightMap.keySet()){
                    if (jump[key2])continue;
                    int pos2 = rightMap.get(key2);
                    if (pos1 > pos2 && (pos1 - pos2) % 2 == 0){
                        flag = true;
                        int t = (pos1 - pos2) / 2;
                        if (t < minTime){
                            minTime = t;
                            list.clear();
                            list.add(key1);
                            list.add(key2);
                        }else if (t == minTime){
                            list.add(key1);
                            list.add(key2);
                        }
                    }
                }
                if (!flag)jump[key1] = true;
            }
            if (list.isEmpty()){
                break;
            }else{
                for (int i : list){
                    ret[i] = minTime;
                    jump[i] = true;
                }
            }
        }
        for (int i = 0; i < n; i++){
            System.out.println(ret[i]);
        }
    }
}

小美的最快到达时间

小美现在临时接到一个会议通知,需要立刻赶往对应地点开会。

不妨将小美所在的地点抽象成一个图。小美位于节点x的位置,将要赶往节点y开会。

小美启动了打车软件,该打车软件可以将起点定位在任何一个节点开始叫车。但是,叫车是需要时间的,不同位置叫车的等车时间不同。

这就意味着,考虑到叫车的时间,小美可以不选自己所在的节点叫车,而选择一个附近的点叫车,在等车时间内自行走路到对应的节点以缩短综合时间,更快地赶到目的地开会。

请注意:小美的叫车终点只能是开会处,即此题不考虑通过多次打车来缩短时间,只考虑更改起点带来的时间减少。

下面给出一个简单的例子来帮助你理解:

小美位于节点1,开会的地点位于节点3

节点1和节点2之间有一条汽车通行时长为1,步行通行时间为2的通路;

节点2和节点3之间有一条汽车通行时长为2,步行通行时间为5的道路;

节点1的打车等候时间为10,节点2的打车等候时间为1,节点3的打车等候时间为5

此时,显然小美有如下几种方案:

第一种方案:小美在节点1打车,此时小美需要先等时间10上车,之后花费3的时间抵达节点3,共计花费时长13;

第二种方案:小美在节点2打车,此时小美需要步行时长2抵达节点2,此时汽车司机已经等候在节点2,小美直接上车,通行时长2后抵达节点3。共计花费时长为4。

第三种方案:小美直接步行到节点3(因为节点3是开会地点,显然在节点3打车无意义),此时花费的时长为7。

以上三种方案中,应选第二种方案能最快抵达开会地点。共计花费时长为4。

注意:实际打车过程中,司机会存在客人太久没来上车自行取消的可能,这里为了简化问题,我们假设司机耐心是充分的,可以无限制等候乘客。

输入描述
第一行四个正整数n,m,x,y,空格隔开,其中 n 表示点的数量,点的序号依次表示为 1 到 n;m表示边的数量;x表示小美当前的节点位置,y表示小美开会的节点位置。

接下来 m 行,每行四个正整数,空格隔开,x, y, p, q,表示节点 x 和节点 y 之间有一条汽车通行时长 p,步行通行时长 q 的双向道路。

接下来一行 n 个空格隔开的正整数,第 i 个正整数表示在第i个节点打车所需要花费的等车时间。

输出描述
输出一行一个正整数表示小美最快抵达开会地点的时间。

样例输入
3 2 1 3
1 2 1 2
2 3 2 5
10 1 5
样例输出
4

提示
数据范围和说明

对于全体数据保证p和q(即汽车通行时间和步行时间)都是[1, 50]内的正整数,保证每个点打车的等候时间都是[1, 1000]内的正整数

对于n和m,对于60%的数据,保证 1<= n <= 10, 1 <= m <= 30, 对于100%的数据,保证 1<= n <= 50, 1 <= m <= 200,数据保证没有重复的边。

学习

没写出来,只碰了9%,动态规划,等我学习明白先


   转载规则


《美团笔试》 锦泉 采用 知识共享署名 4.0 国际许可协议 进行许可。
  目录