网易-2020秋招-笔试题剖析【5道算法题】

网易-2020秋招-笔试题剖析【5道算法题】,限时120分钟。
让我们一起来看看这些题吧!


题一:最小数位和

【题目描述】

定义S(n),表示n在十进制下的各位数字和。

现在给定一个x,请你求出最小正整数n,满足x≤S(n).

【输入描述】

第一行数据组数T,对于每组数据,一行一个数字x。

1≤x≤10^5,1≤T≤10

【输出描述】

对于每组数据,一行一个整数表示最小的n。

【示例】

示例1:

输入:

2
7
9

输出:

7
9

示例2:

输入:

2
13
18

输出:

49
99

【解决思路及要点】

  • 【贪心】n要尽可能小,说明位数要尽可能少,在十进制中9最大,所以对x除9,就可以得到n中9的个数,n的最高位就是余数。(满足n最高位最小,位数最小)
  • 可以直接从最高位开始一位一位的输出,先输出余数,再输出x/9个9不需要再整体转换为数字。

【解决代码】

public class Solution1 {
    public static void main(String[] args) {
        Scanner sc=new Scanner(System.in);
        int T=sc.nextInt();
        while(T-->0){
            int x=sc.nextInt();
            solve(x);
        }
    }
    public static void solve(int x){
        //余数做最高位
        if(x%9>0){
            System.out.print(x%9);
        }
        x-=x%9;
        //x/9就是n中9的个数
        for(int i=1;i<=x/9;i++){
            System.out.print("9");
        }
        System.out.print("\n");
    }
}

【题目剖析】

  • 整个题的思想就是贪心,要n最小,需要满足什么样的条件。
  • 这题属于思维类的题目,应该是比较简单,但如果一开始的思路就往复杂方向去了,这个题可能会花费较多的时间,而且有做不出的风险。
  • 所以写代码前先想想,有没有更简单,更准确的做法。

题二:吃葡萄

【题目描述】

有三种葡萄,每种分别有a,b,c颗。有三个人,第一个人只吃第1,2种葡萄,第二个人只吃第2,3种葡萄,第三个人只吃第1,3种葡萄。
适当安排三个人使得吃完所有的葡萄,并且三个人中吃的最多的那个人吃得尽量少。

【输入描述】

第一行数字T,表示数据组数。

接下来T行,每行三个数a,b,c

1≤a,b,c≤10^18,1≤T≤10

【输出描述】

对于每组数据,输出一行一个数字表示三个人中吃的最多的那个人吃的数量。

【示例】

示例1:

输入:

2
1 2 3
1 2 6

输出:

2
3

示例2:

输入:

1
12 13 11

输出:

12

【解决思路及要点】

  • 如果两种较少葡萄的和比葡萄最多的一半要多,那么可以实现平分。【此时三人平分能够使得吃的最多的那个人吃得尽量少】
  • 如果两种较少葡萄的和比葡萄最多的一半要少,那么结果是最多葡萄的一半。
  • 注意要使用向上取整。(一颗葡萄也要单独吃一次)
  • 也可以看成是三个人分别站在三角形的顶点,假设三角形两个短边是a,b,长边是c。则,若两短边之和大于等于长边的一半,可实现总数平分;反之,则结果为长边的一半。

【解决代码】

public class Solution2 {
    public static void main(String[] args) {
        Scanner sc=new Scanner(System.in);
        int T=sc.nextInt();
        while(T-->0){
            long a,b,c;
            a=sc.nextLong();
            b=sc.nextLong();
            c=sc.nextLong();
            System.out.println(solve(a,b,c));
        }
    }
    public static long solve(long a,long b,long c){
        long maxx=Math.max(Math.max(a,b),c);
        long minn=Math.min(Math.min(a,b),c);
        long mid=a+b+c-maxx-minn;
        if(minn+mid>=maxx/2){
            return up(a+b+c,3);
        }else{
            return up(maxx,2);
        }
    }
    //除法向上取整
    private static long up(long a,long b){
        return a%b>0?a/b+1:a/b;
    }
}

【题目剖析】

  • 总体来说,也是贪心的思想。
  • 这也是一道思维题,需要能抓住合适能够平分,何时是最大值的一半。
  • 类比三角形三边是一种很好的思维。
  • 如果思维不到位的话,容易花了时间而做不对。
  • 如果掌握了这个思路,就可以非常快速的做出了。

题三:圆环切割

【题目描述】

小易有n个数字排成一个环,你能否将它们分成连续的两个部分(即在环上必须连续),使得两部分的和相等?

【输入描述】

第一行数据组数T,对于每组数据
第一行数字n,表示数字个数

接下来一行n个数,按顺序给出环上的数字。

2≤n≤100000,1≤Ai≤10^9

【输出描述】

对于每组数据,一行输出YES/NO

【示例】

示例1:

输入:

1
6
1 2 3 4 5 6

输出:

NO

示例2:

输入:

1
4
4 4 5 3

输出:

YES

【解决思路及要点】

  • 维护一个前缀和数组,将每个前缀和与圆环和的一半做差,如果存在一个前缀和和这个差相等,那么就可以分割。【具体的原理自己在脑海中构造个圆环想一下】这里利用的是圆环起点可变的特性。

【解决代码】

public class Solution3 {
    public static void main(String[] args) {
        Scanner sc=new Scanner(System.in);
        int T=sc.nextInt();
        while(T-->0){
            int n=sc.nextInt();
            int[] pre=new int[n];
            for(int i=0;i<n;i++){
                int x=sc.nextInt();
                pre[i]=(i>0)?pre[i-1]+x:x;
            }
            if(solve(pre)){
                System.out.println("YES");
            }else{
                System.out.println("NO");
            }
        }
    }
    public static boolean solve(int[] pre){
        int n=pre.length;
        //奇数不可能
        if((pre[n-1]&1)>0){
            return false;
        }
        Set<Integer> set=new HashSet<>();
        for(int i=0;i<n;i++){
            //得到pre[i]与圆环和一半的差值
            int s=pre[i]-pre[n-1]/2;
            //若刚好有一个前缀和与这个差值相等,说明可以从前缀中减去,从而构成两部分相等
            if(set.contains(s)){
                return true;
            }
            //将这个前缀和存入set集合
            set.add(pre[i]);
        }
        return false;
    }
}

【题目剖析】

  • 这题思维性不是那么强,算是一个中等偏下的题,需要对前缀和应用比较熟悉。
  • 这个题一步一步做,方法应该还有很多,总体而言不是很难。

题四:跳柱子

【题目描述】

小易有n根柱子,第i根柱子的高度为hi。一开始小易站在第一根柱子上。小易能从第i根柱子跳到第j根柱子,当且仅当hj≤hi且1≤j−i≤k。其中k为指定的一个数字。
另外小易拥有一次释放超能力的机会。这个超能力能让小易从柱子i跳到任意满足1≤j−i≤k的柱子j而无视柱子高度的限制。
现在小易想知道,小易是否能到达第n根柱子。

【输入描述】

第一行数据组数T

对于每组数据,第一行数字n,k接下来一行n个数字表示hi。

1≤n≤1000,1≤hi≤10^9,1≤T≤10,1≤k≤n

【输出描述】

对于每组数据,输出YES或NO

【示例】

示例1:

输入:

1
5 3
6 2 4 3 8

输出:

YES

示例2:

输入:

1
5 2
1 8 2 3 4

输出:

NO

【解决思路及要点】

  • 明显可以使用dp。
  • dp[i][0]表示不使用超能力的情况下是否能到达柱子。
  • dp[i][1]表示使用超能力的情况下是否能到达柱子。
  • 对每个柱子,枚举前面的k个柱子,并更新dp即可。
  • 具体细节见代码。
public class Solution4 {
    public static void main(String[] args) {
        Scanner sc=new Scanner(System.in);
        int T=sc.nextInt();
        while(T-->0){
            int n=sc.nextInt();
            int k=sc.nextInt();
            int[] height=new int[n];
            for(int i=0;i<n;i++){
                height[i]=sc.nextInt();
            }
            if(solve(height,k)){
                System.out.println("YES");
            }else{
                System.out.println("NO");
            }
        }
    }
    public static boolean solve(int[] height,int k){
        int n=height.length;
        boolean[][] dp=new boolean[n][2];
        //初始条件
        dp[0][0]=dp[0][1]=true;
        for(int i=0;i<n;i++){
            for(int j=i-1;j>=0 && j>=i-k;j--){
                //如果第j根柱子不使用超能力能达到
                if(dp[j][0]){
                    //那么使用也是肯定可以的
                    dp[j][1]=true;
                    //如果距离和高度都满足条件,那么第i根柱子在不使用超能力的情况下也是可以达到的
                    if(height[j]>=height[i]){
                        dp[i][0]=true;
                    }
                }
                //如果第j根柱子使用超能力能达到
                if(dp[j][1]){
                    //那么如果满足条件下,第i根柱子在已经使用了超能力的情况下,能够达到
                    if(height[j]>=height[i]){
                        dp[i][1]=true;
                    }
                }
            }
        }
        //最终的答案是,在允许使用超能力的情况下,能否到达最后一根柱子
        return dp[n-1][1];
    }
}

【题目剖析】

  • 算是比较经典的dp,对dp熟悉的话,可以很快的做出。
  • 整体来说,这题不是很难。
  • 应该可以在比较快的时间内做出来。

题五:乘积

【题目描述】

小易给定你一个长度为n的正整数序列Ai,你每次可以使用1的代价将某个数加一或者减一,你希望用最少的代价使得所有数的乘积等于B,求最小代价(操作结束后每个数也必须是正整数)。

【输入描述】

第一行数字n, B,表示序列长度和目标乘积。

接下来一行n个正整数表示初始序列。

1≤n≤10^3 , 1≤B≤ 10^5,1≤Ai≤ 10^5.

【输出描述】

一行一个数字表示答案

【示例】

示例1:

输入:

5 12
1 3 9 2 6

输出:

10

说明:
把3变为1需要2的代价,把9变为1需要8的代价,总代价为10。

示例2:

输入:

3 15
3 8 7

输出:

9

说明:

把8变为5需要3的代价,把7变为1需要6的代价,总代价为9。

【解决思路及要点】

  • dp解决,dp[i][j]表示表示让前i个数字乘积为j的最小代价。
  • 需要大量用到数的因子,故生成一个从1-B的所有数的因子的集合。
  • 具体转移方程和其它细节在注释中。
public class Solution5 {
    public static void main(String[] args) {
        Scanner sc=new Scanner(System.in);
        int n=sc.nextInt();
        int B=sc.nextInt();
        int[] a=new int[n+1];
        for(int i=1;i<=n;i++){
            a[i]=sc.nextInt();
        }
        System.out.println(solve(a,B));
    }
    public static int solve(int[] a,int B){
        int n=a.length-1;
        //该集合是1-B的所有数的因子的集合
        List<List<Integer>> v=new ArrayList<>();
        //初始化集合
        for(int i=0;i<=B;i++){
            v.add(new ArrayList<>());
        }
        //得到1-B所有数的因子的集合
        for(int i=1;i<=B;i++){
            for(int j=i;j<=B;j+=i){
                v.get(j).add(i);
            }
        }
        //m为B的因子的个数
        int m=v.get(B).size();
        int[] num=new int[B+1];
        //从前往后记录B的因子的下标【等于可以从一个因子得到相应的下标,也可以用哈希表实现】
        for(int i=0;i<m;i++){
            num[v.get(B).get(i)]=i;
        }
        //dp数组
        int[][] dp=new int[n+1][m];
        //初始化dp[0]为最大值
        for(int i=1;i<m;i++){
            dp[0][i]= (int) 1e9;
        }
        for(int i=1;i<=n;i++){
            for(int j=0;j<m;j++){
                //初始化dp[i][j]为最大值
                dp[i][j]= (int) 1e9;
                //k遍历B的第j个因子的所有因子
                for(int k=0;k<v.get(v.get(B).get(j)).size();k++){
                    //dp[i-1][num[v.get(v.get(B).get(j)).get(k)]]是前i-1个数乘积为B的第j个因子的第k个因子的最小代价
                    //Math.abs(a[i]-v.get(B).get(j)/v.get(v.get(B).get(j)).get(k))是 第i个数-B的第j个因子/B的第j个因子的第k个因子 的绝对值
                    //这两个之和就是转换的最小代价
                    dp[i][j]=Math.min(dp[i][j],dp[i-1][num[v.get(v.get(B).get(j)).get(k)]]
                            +Math.abs(a[i]-v.get(B).get(j)/v.get(v.get(B).get(j)).get(k)));
                }
            }
        }
        //最终答案是前n个数字乘积为B的最小代价
        return dp[n][m-1];
    }
}

【题目剖析】

  • 这个题开始我是完全没有做出来,想到dp的思路,但始终找不到转移的点。
  • 这个题感觉还是比较难的dp了,估计在两小时内做出的人比较少,这场比试真正拉开算法能力强的人与普通人差距的题目。
  • 算是一个难度dp了。

该次笔试题感悟:

  • 难度和顺序基本一致。
  • 这次笔试主要考察的是思维【贪心,dp出现次数多】,题目都简短,但实际做却感觉比较的难。
  • 两个小时的时间,前四题拿下的可能性很大,最后一题就看命了。
  • 足以可以看出,算法还是不要只顾刷题,思维才是最重要的点!

本文全部代码已上传至github仓库,欢迎大家Star一波!

链接:https://github.com/ATFWUS/Algorithm-Pen-Test-2020/tree/master/%E7%BD%91%E6%98%93%E7%A7%8B%E6%8B%9B%E7%AC%94%E8%AF%95%E9%A2%98

ATFWUS

猜你喜欢

转载自blog.csdn.net/ATFWUS/article/details/106828568