第2天:网易2018年校园招聘NLP算法工程师笔试试卷分析(二)

前言

  今天又做了一套网易2018校招NLP算法工程师笔试卷 ,这次只有三道笔试编程题,其中有一道还是比较简单的。只是简单的一道数学题的应用。接下来的两道题就比较难了,一道是考察堆棋子的问题,另外一道就是定义一种新的数列,通过算法来实现这种数列,整体而言,都比较难的。接下来给大家详细解答这三道题。

试题分析

一、编程题

1、题目描述:

小易为了向他的父母表现他已经长大独立了,他决定搬出去自己居住一段时间。一个人生活增加了许多花费: 小易每天必须吃一个水果并且需要每天支付x元的房屋租金。当前小易手中已经有f个水果和d元钱,小易也能去商店购买一些水果,商店每个水果售卖p元。小易为了表现他独立生活的能力,希望能独立生活的时间越长越好,小易希望你来帮他计算一下他最多能独立生活多少天。

具体要求:

时间限制:C/C++ 1秒,其他语言2秒
空间限制:C/C++ 32M,其他语言64M

输入描述:

输入包括一行,四个整数x, f, d, p(1 ≤ x,f,d,p ≤ 2 * 10^9),以空格分割

输出描述:

输出一个整数, 表示小易最多能独立生活多少天。

输入例子1:

3 5 100 10

输出例子1:

11

具体思路
  其实本题比较简单,就是数学的简单应用。具体来说分为两种情况:

  • 有f个水果,先看交f天房租钱花的完不。
  • 如果花的完,天数就是可以租房的天数。
  • 如果花不完,天数是 f加上交f天房租后剩余的钱数除以买水果的钱加一天房租钱。
    具体的实现如下:
import java.util.Scanner;
public class Main {
    public static void main(String[] args) {
        Scanner sc = new Scanner(System.in);
        long x=sc.nextLong();
        long f=sc.nextLong();
        long d=sc.nextLong();
        long p=sc.nextLong();
        sc.close();
        long maxDay=0;
        if(d-f*x>0) 
        {
            long tmp= (d-f*x)/(x+p);
            maxDay=tmp+f;
        }
        else    
        {
            maxDay=d/x;
        }
        System.out.println(maxDay);
    }
}

具体效果如图所示:

  当然我们也可以用python实现,其代码更为漂亮简洁。具体实现如下:

x, f, d, p = [i for i in map(int, input().split())]  
if d < f * x:
    print(d // x)
else:
    print(f + (d - x * f) // (p + x))

具体效果如图所示:

2、题目描述:

小易将n个棋子摆放在一张无限大的棋盘上。第i个棋子放在第x[i]行y[i]列。同一个格子允许放置多个棋子。每一次操作小易可以把一个棋子拿起并将其移动到原格子的上、下、左、右的任意一个格子中。小易想知道要让棋盘上出现有一个格子中至少有i(1
≤ i ≤ n)个棋子所需要的最少操作次数.

具体要求:

时间限制:C/C++ 1秒,其他语言2秒
空间限制:C/C++ 32M,其他语言64M

输入描述:

输入包括三行,第一行一个整数n(1 ≤ n ≤ 50),表示棋子的个数
第二行为n个棋子的横坐标x[i](1 ≤ x[i] ≤ 10^9)
第三行为n个棋子的纵坐标y[i](1 ≤ y[i] ≤ 10^9)

输出描述:

输出n个整数,第i个表示棋盘上有一个格子至少有i个棋子所需要的操作数,以空格分割。行末无空格
如样例所示:
对于1个棋子: 不需要操作
对于2个棋子: 将前两个棋子放在(1, 1)中
对于3个棋子: 将前三个棋子放在(2, 1)中
对于4个棋子: 将所有棋子都放在(3, 1)中

输入例子1:

4
1 2 4 9
1 1 1 1

输出例子1:

0 1 3 10

具体思路
  本题的关键是找到一个最优的聚合点,使得各个棋子到这个聚合点的距离最短。由于x和y轴是相互独立的,互不影响,因此可以先分析x轴再分析y轴。以[1,2,4,9]为例,根据相关证明,最优聚合点的x坐标一定是 [1,2,4,9]这几个数之一,同理y坐标一定是[1,1,1,1]这几个数之一。有了这个结论,就可以使用暴力法,一一枚举每一个可能的点并计算距离,求出距离最小的那个点。为了证明上面的结论,我们使用反证法:

   假设存在点x0使得其余各点到x0的距离最短,并且x0不是[1,2,4,9]之一。以x0=6为例,在x0左边有[1,2,4]共3个数,我们把3记为a;在x0右边有[9]共1个数,把1记为b。[1,2,4]到x0的距离记为A;[9]到x0的距离记为B;那么总距离就为A+B。
   当a > b时,我们发现x0左边第一个在[1,2,4,9]集合中的数,会得到更小的距离。即:x0等于4时,总距离是:A+B-a * 2 + b * 2,其中a等于3,b等于1。所以我们之前的假设不成立.同理,当a < b时,x0右边第一个在[1,2,4,9]集合中的数,也会使总距离变小。

   当a == b时,“x0等于3”与 “x0等于2” 或者 “x0等于4”时的总距离相等,因此“x0等于3”就可以被“x0等于3”或“x0等于4”替代。

   总之, 如果我们最开始的假设不成立,x0必然是[1,2,4,9]之一,同理y也必然出现在[1,1,1,1]之一。

  通过上面证明的结论,我们可以采用暴力法来实现该题,具体代码用java实现如下:

import java.util.*;
public class Main {
    public static int[] solution(int n, int[] x, int[] y) {
        int[] result = new int[n];
        int[][][] distance = new int[n][n][n];
        for(int p = 0; p < n; p++) {
            for(int q = 0; q < n; q++) {
                for(int i = 0; i < n; i++) {
                    distance[p][q][i] = Math.abs(x[p] - x[i]) + Math.abs(y[q] - y[i]);
                }
            }
        }
        for(int p = 0; p < n; p++) {
            for(int q = 0; q < n; q++) {
                Arrays.sort(distance[p][q]);
            }
        }
        for(int i = 1; i < n; i++) {
            int min = Integer.MAX_VALUE;
            for(int p = 0; p < n; p++) {
                for(int q = 0; q < n; q++) {
                    int count = 0;
                    for(int k = 0; k < i+1; k++) {
                        count += distance[p][q][k];
                    }
                    min = Math.min(min, count);
                }
            }
            result[i] = min;
        }
        return result;
    }
    public static void main(String[] args) {
        Scanner sc = new Scanner(System.in);
        while(sc.hasNext()) {
            int n = sc.nextInt();
            int[] x = new int[n];
            int[] y = new int[n];
            for(int i = 0; i < n; i++) {
                x[i] = sc.nextInt();
            }
            for(int i = 0; i < n; i++) {
                y[i] = sc.nextInt();
            }
            int[] result = solution(n, x, y);
            StringBuilder sb = new StringBuilder();
            for(int i = 0; i < n - 1; i++) {
                sb.append(result[i]).append(" ");
            }
            sb.append(result[n-1]);
            System.out.println(sb.toString());
        }
        sc.close();
    }
}

具体效果图如图所示:

当然以上的思路我们也可以用python将其实现,不过这里需要三重循环嵌套。

n = int(input())
x_cords = [i for i in map(int, input().split())]
y_cords = [j for j in map(int, input().split())]
results = [99999999] * n
for x_cord in x_cords:
    for y_cord in y_cords:
        accumulator = 0
        dis_list = sorted([abs(x_cord - x_cords[i]) + abs(y_cord - y_cords[i]) for i in range(n)])
        for i in range(n):
            accumulator += dis_list[i]
            if accumulator <= results[i]:
                results[i] = accumulator 
print(' '.join(map(str, results)))

具体效果图如图所示:

3、题目描述:

小易非常喜欢拥有以下性质的数列:
1、数列的长度为n
2、数列中的每个数都在1到k之间(包括1和k)
3、对于位置相邻的两个数A和B(A在B前),都满足(A <= B)或(A mod B != 0)(满足其一即可)
例如,当n = 4, k = 7
那么{1,7,7,2},它的长度是4,所有数字也在1到7范围内,并且满足第三条性质,所以小易是喜欢这个数列的
但是小易不喜欢{4,4,4,2}这个数列。小易给出n和k,希望你能帮他求出有多少个是他会喜欢的数列。

具体要求:

时间限制:C/C++ 1秒,其他语言2秒
空间限制:C/C++ 32M,其他语言64M

输入描述:

输入包括两个整数n和k(1 ≤ n ≤ 10, 1 ≤ k ≤ 10^5)

输出描述:

输出一个整数,即满足要求的数列个数,因为答案可能很大,输出对1,000,000,007取模的结果。

输入例子1:

2 2

输出例子1:

3

具体思路
  其实这道题考察的是动态规划的思想,但是刚开始没那么明显,因此需要我们从简单的开始找一规律来理解本题。
(1)、n = 3, k = 3时

最后总个数 = 1 + 7 + 7。
(2)、n = 3, k = 4时

最后总个数 = 1 + 8 + 12 + 12 = 33
  最后发现该题考察的就是动态规划,因此理清思路之后我们用java实现:

import java.util.Scanner;
public class Main {
    public static int M = 1000000007;
    public static int solution(int n, int k) {
        int[][] dp = new int[k][n];
        for(int i = 0; i < k; i++) {
            dp[i][0] = 1;
        }
        for(int i = 0; i < n; i++) {
            dp[0][i] = 1;
        }
        for(int j = 1; j < n; j++) {
            int sum = 0;
            for(int i = 0; i < k; i++) {
                sum = (sum + dp[i][j-1]) % M;
            }
            for(int i = 0; i < k; i++) {
                int sum_invalid = 0;
                int p = 2;
                while(p * (i+1) <= k) {
                    sum_invalid = (sum_invalid + dp[p*(i+1)-1][j-1] ) % M;
                    p++;
                }
                dp[i][j] = (sum - sum_invalid + M) % M;
            }
        }
        int result = 0;
        for(int i = 0; i < k; i++) {
            result += dp[i][n-1];
            result %= M;
        }
        return result;
    }
    public static void main(String[] args) {
        Scanner sc = new Scanner(System.in);
        int n = sc.nextInt();
        int k = sc.nextInt();
        int result = solution(n, k);
        System.out.println(result);
        sc.close();
    }
}

代码通过示意图如图所示:

  本来还可以用python实现,但是写出的代码通过率竟然是只有40%,在网上找了本题关于python的代码,但是通过最多也是60%,因此,为了不给大家带来困扰,就不上传python代码了。如果想了解python实现代码的童鞋可以看文章,写的挺好,可惜没通过。

二、问答题

1、题目描述

假设某台服务器上有一个10亿行文本的文件,文件中每一行都是已经经过分词的句子。现在需要统计该文件中每个词出现的频次,现在有另有5台服务器供你使用,你能否设计流程,充分利用这些服务器,尽可能的快速统计出词语频次?
(请重点描述文件按何种方式分割到多台服务器,每台服务器计算出什么样的结果,这些结果又是按何种方式聚合到一起得到我们最终需要的词频统计信息的)

注意:本题暂不支持系统判断,可在交卷后查看参考答案。
由于本题没有标准答案,我就不好意思把自己的答案贴出来了,不过考察的点就是自然语言处理中的词频统计在大数据下的应用。希望这篇文章可以给你思路。
2、题目描述

1、请言简意赅地描述一个你所参与的NLP相关项目,包括项目的目标(需要解决的问题)以及系统架构,然后详述一个你最熟悉的模块。
2、如果上述模块是采用传统的机器学习算法(LR,GBDT,SVM等),请你结合应用谈谈你是如何设计和选择特征的;如果采用的是深度学习方法,请你说明此项目中使用的深度学习算法具有什么样的优势?
3、在该项目中是否使用某些开源的工具或者框架,对比同类工具和框架他们具有什么特点?
4、描述你在参与项目中遇到的主要困难以及你的解决办法。

注意:本题暂不支持系统判断,可在交卷后查看参考答案。 由于本题的答案在上一篇文章中给过,因此就不贴了,如果感兴趣的可以查看此文章

总结

  我们现在已经模拟了两套网易公司的历年真题,通过对公司真题的解答发现,公司的题目还是整体很注重基础算法的,并且考察的比较全面,着重考察的是我们对数据结构与算法的掌握程度,和我们平常练习的有所差别,我们还得继续努力,掌握好每个知识点。并且要学会学以致用。秋招已经启动,该开始刷题了,好好准备,希望2021届毕业的我们都能有一份满意的Offer,Hang in there,you can do this.Time will there, Break a leg!!!

参考文献

[1] 网易笔试:堆棋子
[2] 网易2018校招内推编程题集合:堆棋子 [python]
[3] 网易笔试:小易喜欢的数列

猜你喜欢

转载自blog.csdn.net/Oliverfly1/article/details/107574145