美团点评实习生算法岗笔试题 (java) 2020.04.23场

T1 幸运星

题目描述

在观星的时候,一种常用的方式是划出类似于正方形的区域内,确定其中所有星星的坐标。
现在我们在星空(一个无限大的二维平面)上简历坐标系。由于星星很小,我们忽略它的面积,认为每一个星星是一个点,且所有星星的坐标都是整数。
幸运星的定义是这一颗星星在这个平面内,正上,正下,正左,正右都有其他的星星(不一定相邻)。
现在,我们已经将这个正方形的取余取出,并且将她们所在的坐标给你。现在希望你能计算,这个平面内有多少颗幸运星?

输入

输入第一行包含一个数n,代表正方形区域内星星的总数。
接下来n行,每行两个整数 x i x_{i} xi y i y_{i} yi,代表这颗星星的坐标。
( n ⩽ 2000 , − 1000 ⩽ x i , y i ⩽ 1000 n \leqslant 2000, -1000 \leqslant x_{i},y_{i}\leqslant 1000 n2000,1000xi,yi1000,没有两颗星星的坐标是相同的。)

输出

输出包含一个数,即有多少颗星星是幸运星。

样例输入

8
0 0
0 1
0 2
0 3
1 1
1 2
-1 1
-1 2

样例输出

2

提示

样例解释:
有两颗幸运星,分别是(0,1),(0,2)

题解:暴力枚举ac,可怕吧?

import java.util.Scanner;
public class Main {
    public static void main(String[] args) {
        Scanner sc = new Scanner(System.in);
        int n = sc.nextInt();
        int[][] sky = new int[n][2];
        for (int i = 0; i < n; i++) {
            sky[i][0] = sc.nextInt(); // 横坐标
            sky[i][1] = sc.nextInt(); // 纵坐标
        }
        int res = 0;
        for (int i = 0; i < n; i++) {
            int x = sky[i][0];
            int y = sky[i][1];
            boolean up = false;
            boolean down = false;
            boolean left = false;
            boolean right = false;
            for (int j = 0; j < n; j++) {
                if(i == j) continue;
                if(sky[j][0] == x){ // 横坐标相等
                    if(sky[j][1] < y){ // j在i的下边
                        down = true;
                    }else{ // j在i的上边
                        up = true;
                    }
                }
                if(sky[j][1] == y){ // 纵坐标相等
                    if(sky[j][0] < x){ // 右边
                        right = true;
                    }else{ // 左边
                        left = true;
                    }
                }
            }
            if(up && down && left && right){
                res++;
            }
        }
        System.out.println(res);
    }
}

T2 规范化货币

题目描述

货币数值的规范化是金融公司的一个问题,现在你需要写一个程序来解决这一问题:

  1. 货币数值的整数部分要求每3位加一个英文‘,’(不含引号)。例如12345678应该规范为12,345,678
  2. 货币数值最多只有两位小数,如果有多余的小数位数应当舍去。注意,不是四舍五入。
  3. 负数代表欠款,在规范化后应当在数值两端加上括号’(’ 和 ‘)’ ,然后省略掉负号。
  4. 应当在数值前面,前括号后面(如果有括号的话)加上金钱符号’$’(不含引号)
    现在给你一个数字,请你规范化这一数字

输入

输入包含多种数据,每组数据一行一个数字,可能为小数,整数,负整数,负小数或者零。
数据保证数字没有前导0,保证不会出现欠0元的情况。

输出

输出规范化后的内容

样例输入

203323
0.0
0.00000
0.009212121
343444323.32432
-12344.1
-12345678.9

样例输出

$203,323.00
$0.00
$0.00
$0.00
$343,444,323,32
($12,344.10)
($12,345,678.90)

提示

范围
每个字符串长度不会超过100

题解
常规思路,注意语法。

import java.util.Scanner;

public class ex2 {
    public static void main(String[] args) {
        Scanner sc = new Scanner(System.in);
        while (sc.hasNext()) {
            String str = sc.next();
            StringBuilder sb = new StringBuilder(str);
            int pointIndex = sb.indexOf(".");
            if (pointIndex == -1) sb.append(".00");
            else {
                // 将小数点后保留两位
                int i = pointIndex;
                if (i + 2 < sb.length() - 1) { // .后面位数大于2,比较索引
                    sb.replace(i + 3, sb.length(), "");
                } else { // 相等或不足2位
                    while (i + 2 > sb.length() - 1) {
                        sb.append("0");
                    }
                }
                // 将小数点前用","隔开
                i = i - 3; // 如果要加",",应加在i处
                while (i - 1 >= 0 && Character.isDigit(sb.charAt(i - 1))) { // 如果i前面不是空,也不是符号,则加","
                    sb.insert(i, ",");
                    i -= 3;
                }
                // 如果是负数,加()和$
                if (sb.charAt(0) == '-') {
                    sb.replace(0, 1, "$");
                    sb.insert(0, "(");
                    sb.append(")");
                } else { // 如果不是负数
                    sb.insert(0, "$");
                }
                System.out.println(sb.toString());
            }
        }
    }
}

T3 轮流出局

题目描述

现在有n名选手进行轮流报数,选手按顺序编号为1~n,另外我们会给出一个序列A,游戏会进行n轮,每轮会出局一名选手,第i轮淘汰的选手最后的排名是n-i+1,即第一轮出局的是倒数第一。出局的选手不会参与下一轮报数。
每轮游戏都是从第一个选手开始报数,即如果1号选手仍在,则从1号选手开始,否则从2号选手开始,以此类推,但是注意,每轮报数是从0开始的,第i轮时,第一个报到A[i]的选手会出局,且当前轮游戏结束。A[i]有可能大于当前的剩余人数,则最后一个人报完以后,会由第一个人接着报,知道报出A[i]。

输入

输入第一行包含一个正整数n,表示有n名选手。(1<= n<=100000)
输入第二行包含n个正整数,表示序列A。(0 <= A[i]<= 10^9)

输出

输出包含n行,每行一个正整数,第i行的正整数表示i号选手的排名是多少。即输出是一个1~n的排列。

样例输入

4
1 2 1 2

样例输出

1
4
2
3

提示

样例解释
第一轮中,1-4号选手报数分别是0,1,+,+(+代表未报数),因为A[1]=1,所以2号选手出局,排名为4。
第二轮中,1-4号选手报数为0,-,1,2(-代表出局),因为A[2]=2,所以4号选手出局,排名为3。
第三轮中,1-4号选手报数为0,-,1,-,因为A[3]=1,所以3号选手出局,排名为2.
第四轮中只有1号选手了,所以他会报所有的数字,最后出局。

题解思路
按题目的要求做,用一个数组记录是否出局,另一个数组记录出局人的排名
注意为了防止越界,index要模n。另外一个细节是:每次从第一个人开始报数!

import java.util.Scanner;

public class Main {
    public static void main(String[] args) {
        Scanner sc = new Scanner(System.in);
        int n = sc.nextInt();
        int[] signal = new int[n]; // 未出局记为0,出局记为1
        int[] res = new int[n]; // 记录对应位置是第几个出局的人
        int index = 0;
        for (int i = 0; i < n; i++) { // 找出第i个人出局的人。
            int count = sc.nextInt();
            index = 0; // 每一轮是从第一个选手开始报数
            for (int j = 0; j < count; j++) {
                index = (index + 1) % n;
                while (signal[index] == 1) {
                    // 如果当前index位置已出局,index后移一位
                    index = (index + 1) % n;
                }
                // index是当前数j的位置
            }
            signal[index] = 1; // 将出局人的位置index记为1
            res[index] = n - i; // index处记录排名
        }
        // 按位置输出排名
        for (int i = 0; i < n; i++) {
            System.out.println(res[i]);
        }
    }
}

T4 最优购买策略

题目描述

熊爷爷的超市正在打折活动当中!
目前,你和你的家里人一共k个人一起去买生活用品。由于打折活动力度很大,每个人只能去付款一次,但是这一次买的东西价格是不做限制的。
熊爷爷的超市物品分为两类:A类和B类物品,活动是如果一个人买的商品中含有A类物品,那么他买的所有物品中最便宜的一件物品半价。如果一个人买的商品中只有B类物品,那么他买的物品不打折。
你们计划要买n个物品,现在将这n个物品的信息给你,请你计算如何分配k位家人比较和算。

输入

第一行有两个整数n,k,代表物品的数量和人的数量。
接下来n行,每行两个整数u,v描述一个物品。u代表物品的价格,v代表商品的种类。如果v为1,代表其为A类商品。如果v为2,代表其为B类商品。
1<= n, k <= 1000, 1<= u<= 1000000, v ∈ \in {1, 2}

输出

输出一行一个小数,表示所需要的最少的钱数。保留两位小数输出。

样例输入

5 2
10 1
2 2
5 2
8 1
9 1

样例输出

28.00

提示

样例解释:
第一个人只买第一个物品,第二个人买剩下的物品。
第一个人由于买了A类物品,最便宜的物品半价,付款5元。
第二个人由于也买了A类物品,最便宜的物品半价,付款23元(物品二半价)。
(买法不唯一)

题解
使用贪心法则
如果现在只有一个人,没有选择,只能查看是否有A,有A的情况下,省钱为minPrice/2.
如果有2个人,那么第一个人要怎么分,才能使省的钱最多呢?
首先第二个人最多可以省maxA/2,所以肯定要分出最大的A给第二个人,并且保证第二个人省maxA/2,那么自己还有没有A呢?如果有,也省钱minPrice/2。如果没有,则不省钱了,但总体来说,省的钱从minPrice/2变成了maxA/2,省的钱变多了。而且可以验证是这个策略分钱是最多的。那么怎么保证第二个人省priceA/2呢?保证A是里面价格最小的即可:分给第二个人的B都大于等于A,或者只给第二个人分A不分B,B留在第一个人手里,不会有任何影响,因为我们只用考虑该单中的最小值。
扩展到k个人的情况,为了减少计算量,我们采取以下策略:
每一次都将剩下的商品里最大的A单独分给一个人买单,保证省钱是maxA/2。
重复这个操作,如果过程中A没了,就再也不享受折扣了。
最后一单包含了所有B商品和剩余A,也可能没有A了。
如果到最后一单还有A,要特别考虑,因为没办法保证A是最后一单里价格最小的,所以这一单省的钱是min{A,B}。
所需最小的钱数 = 总价 - 省钱最多。
结束。
分析复杂度
需要给A降序排列,并且找到最小的B。如果k<

import java.util.Scanner;
import java.util.ArrayList;
import java.util.Collections;

public class Main {
    private static int MIN = (int) 1e6 + 10;

    public static void main(String[] args) {
        Scanner sc = new Scanner(System.in);
        int n = sc.nextInt(); // n个物品
        int k = sc.nextInt(); // k个人
        int min = MIN;
        int sum = 0; // 记录总价
        ArrayList<Integer> Aprice = new ArrayList<>();
        ArrayList<Integer> Bprice = new ArrayList<>();
        for (int i = 0; i < n; i++) {
            int price = sc.nextInt();
            int kind = sc.nextInt();
            if (kind == 1) { // 物品i是A类
                Aprice.add(price);
                sum += price;
            } else { // 物品i是B类
                min = Math.min(min, price); // 找出B类中价格最小的
                Bprice.add(price);
                sum += price;
            }
        }

        // A需要排序,B只需要找出最小值。
        Collections.sort(Aprice); // A从小到大排序
        int count = Aprice.size() - 1;
        double res = 0; // 记录可以省下的钱总数
        while (k > 0 && count >= 0) { // 还有人,并且还有A物品
            if (k == 1) { // 只剩下1个人,需买下剩下的A物品与所有B物品
                res += (double) Math.min(Aprice.get(0), min) / 2;
                break;
            } else {
                res += (double) Aprice.get(count) / 2;
                count--;
                k--;
            }
        }
        System.out.printf("%.2f",sum-res);
    }
}

T5 相似

题目描述

两个数是相似的,当且仅当他们位与起来不为0。例如3和5是相似的,因为3的二进制为011,5的二进制为101,他们位与起来为001不为0。
现在,给序列a1, a2, … , an我们希望你找出,对于任意i ∈ \in [1,n],是否存在j ∈ \in [1,n],使得ai,aj不相似。

输入

输入第一行包括一个整数n,代表序列a的长度
接下来一行n个整数,空格隔开,代表序列a
1<=n<=100000, 1<=ai<=106

输出

输出n个数
如果对于i个数,存在j ∈ \in (1, n],使得ai, aj不相似,输出1,否则输出-1

样例输入

4
3 5 6 1

样例输出

-1 -1 1 1

提示

样例解释
唯一一对不相似的数是6和1,故6和1的答案为1,其余为-1。

题解
我没有做到这一题,看dalao们做的,好多都是暴力枚举然后通过45%case。
我觉得原因可能是,n非常大,但ai并不是很大,所以在遍历少数n的时候,就可以找到不相似的了【只是一种可能性】,如果找到了就即时退出吧,别再找下去了。
我感觉我做的也是暴力枚举,所以非常不安,感觉写了个错误答案。如果有dalao知道ac方法,请告诉我吧。
大致思路就是,如果a&b==0–>a与b不相似,例如1&6==0,1是001,6是110;2&6==1,2是010,6是110。

import java.util.Scanner;
public class Main {
    public static void main(String[] args) {
        Scanner sc = new Scanner(System.in);
        int n = sc.nextInt();
        int[] arr = new int[n];
        int[] res = new int[n]; // 记录结果,初始化都是-1(假设找不到不相似)
        for (int i = 0; i < n; i++) {
            arr[i] = sc.nextInt();
            res[i] = -1;
        }
        for (int i = 0; i < n; i++) {
            if(res[i] != 1){ // 已经有不相似的了,不用再找
                for (int j = 0; j < n; j++) {
                    if(i != j && (arr[i]&arr[j]) == 0){ // i,j不相似
                        res[i] = 1;
                        res[j] = 1;
                        break;
                    }
                }
            }
        }
        // 遍历res输出结果
        for (int i = 0; i < res.length-1; i++) {
            System.out.print(res[i]+" ");
        }
        System.out.print(res[res.length-1]);
        System.out.println();
    }
}

你可能感兴趣的:(美团,算法,笔试题)