網易-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

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章