网易2017 实习生笔试题

Posted 安安zoe

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了网易2017 实习生笔试题相关的知识,希望对你有一定的参考价值。

1.【编程题】消除重复元素

  时间限制:1秒

  空间限制:32768K

  小易有一个长度为n序列,小易想移除掉里面的重复元素,但是小易想是对于每种元素保留最后出现的那个。小易遇到了困难,希望你来帮助他。 

  输入描述

  输入包括两行:
  第一行为序列长度n(1 ≤ n ≤ 50)
  第二行为n个数sequence[i](1 ≤ sequence[i] ≤ 1000),以空格分隔

  输出描述

  输出消除重复元素之后的序列,以空格分隔,行末无空格

  输入例子

  9
  100 100 100 99 99 99 100 100 100

  输出例子

  99 100

题目分析

1.解法一:这道题目里面序列长度并不长,最多不会超过50个,因此使用搜索的方式也可以,虽然效率不高,但是可以使用。参考其他人的代码,对于去重有一种技巧,这道题目是从后向前搜索,因此在搜索时候,将重复的数字置位,这里因为数的范围在1-1000,因此置位为0.(注意审题。)代码如下:

#include<iostream>
using namespace std;

int main()
{
    int n;
    cin >> n;
    int c[50];
    for (int i = 0; i < n; i++)
        cin >> c[i];

    //倒序
    for (int i = n - 1; i >= 0; i--)
    {
        int b = c[i];
        for (int j = i - 1; j >= 0; j--)
        {
            if (c[j] - b == 0)
                c[j] = 0;
        }
    }

    //输出
    for (int i = 0; i < n; i++)
    {
        if (c[i] != 0)
        {
            cout << c[i];
            if (i < n - 1)
                cout << \' \';
        }
    }
    return 0;
}

 2.解法2:这里涉及到一个数据存储的问题,提炼出本质:就是说从后向前搜索,将数据存入数据结构中,首先判重复,检查已经存下的数据中是否有该数据,如果重复就不加入数据结构中。

  有人使用队列结构:使用一个队列,对于每次输入,判断该数是否已经存在,若存在,删除。然后压入队列。

  但更多得是使用set:set是一种关容器,是关键字的简单集合,当只想知道一个值是否存在的时候,set是最有用的。

#include <iostream>
#include <vector>
#include <set>
using namespace std;

int main()
{
    vector<int> input, res;
    set<int> s;
    int n,x;
    //输入
    cin >> n;
    for (int i = 0; i < n; i++)
    {
        cin >> x;
        input.push_back(x);
    }

    //set 查重  从后向前查重
    for (int i = input.size() - 1; i >= 0; i--)
    {
        if (s.find(input[i]) == s.end())  //s.end()
        {
            s.insert(input[i]);
            res.push_back(input[i]);//注意set会对元素顺序根据关键字改变 因此需要存放到vector中
        }
    }
  //倒序输出
    cout << res[res.size() - 1];
    for (int i = res.size() - 2; i >= 0; i--) 
        cout << " " << res[i];

    return 0;
}

 2.【编程题】双核处理

  时间限制:1秒

  空间限制:32768K

  一种双核CPU的两个核能够同时的处理任务,现在有n个已知数据量的任务需要交给CPU处理,假设已知CPU的每个核1秒可以处理1kb,每个核同时只能处理一项任务。n个任务可以按照任意顺序放入CPU进行处理,现在需要设计一个方案让CPU处理完这批任务所需的时间最少,求这个最小的时间。
  输入描述:
  输入包括两行:
  第一行为整数n(1 ≤ n ≤ 50)
  第二行为n个整数length[i](1024 ≤ length[i] ≤ 4194304),表示每个任务的长度为length[i]kb,每个数均为1024的倍数。

  输出描述:

  输出一个整数,表示最少需要处理的时间

   输入例子:

  5
  3072 3072 7168 3072 1024

   输出例子:9216

题目分析和解答

  1.解法1:这道题初拿到感觉到有点难下手,后来正常想,假如说所有任务需要的处理的时长的Task-sum,双核的处理器的情况下,每一个处理器处理Task-sum/2的情况下就是最优的状态,但是事实上单个任务的Task不可分割,所以应该尽量双核工作的时长应该尽量逼近Task_sum/2。这里实际上就转换为0-1背包问题。

  这里的解法就是非常中规中矩的动态规划,在空间上有较大的可优化空间,具体见解法2.在做解题的时候有几个注意点:

  (1)数组的长度不定,背包(内核的处理时长Task_sum/2)容量也需要根据测试用例来确定,因此需要动态申请二维数组,也需要在最后动态释放空间。申请的二维数组的长度也需要注意一下,边界条件和填表时递增的初始值也要注意。

  (2)另外一个容易出错点:我在做题的时候,输入进来的数据用的是vector,下标是(0-n-1),但是在用的时候,下标用的是(1-n),因此一开始的时候通过率为90%,后来发现是我的下标弄错的原因,所以做题目一定要仔细。

  (3)最后一个注意时:就是最后的结果,实际上获得的结果res是对于一个内核来说,最逼近(task_sum/2)的状态,它不可能超过task_sum/2,因此所取的值一定是task_sum - res。

#include <iostream>
#include <algorithm>
#include <vector>
using namespace std;
 
int main()
{
    int n = 0; //任务总数;
    int t;
    int sum=0,half =0;
    vector<int> task;
 
    //输入
    cin >> n;
    task.push_back(0);  // task 的第一位
    for (int i = 0; i < n; i++)
    {
        cin >> t;
        t = t/1024;
        sum += t;
        task.push_back(t);
    }
    half = sum / 2;
     
    // 申请二维数组
    int **c = new int *[n+1];  //+1
    for (int i = 0; i <=n; i++)
        c[i] = new int [half+1];
  
    for (int i = 0; i <= n; i++)
        c[i][0] = 0;
    for (int j = 0; j <= half; j++)
        c[0][j] = 0;
 
    for (int i = 1; i <= n; i++)
    {
        for (int j = 1; j <= half; j++)
        {
         
            if (task[i] > j)
                c[i][j] = c[i - 1][j];
            else
                c[i][j] = max(c[i - 1][j - task[i]] + task[i], c[i - 1][j]);
        }
    }
  
    cout<<(sum-c[n][half])*1024;
    
    //删除申请的二维数组
    for(int i =0;i<=n;i++)
        delete []c[i];
    delete []c; 
    return 0;     
}

 

  2.解法2:优化的动态规划,在空间上进行优化;

#include <iostream>
#include <algorithm>
#include <vector>
using namespace std;
 
int main()
{
    int n = 0; //任务总数;
    int t;
    int sum =0,half =0;
    vector<int> task;
 
    //输入
    cin >> n;
    for (int i = 0; i < n; i++)
    {
        cin >> t;
        t = t/1024;
        sum += t;
        task.push_back(t);
    }
    half = sum / 2;
    
    int *dp = new int[half+1];  //+1
    for(int i =0;i<=half;i++ )
        dp[i]=0;
      
    for(int i =0;i<n;i++)  //注意 for里面
    {
        for(int j = half;j>=task[i];j--)
        {
            dp[j] = max(dp[j],dp[j-task[i]]+task[i]);  
        }        
    }
    cout<<(sum-dp[half])*1024;
    delete []dp;
    return 0;     
}

3.[编程题] 赶去公司

  时间限制:1秒

  空间限制:32768K

  终于到周末啦!小易走在市区的街道上准备找朋友聚会,突然服务器发来警报,小易需要立即回公司修复这个紧急bug。假设市区是一个无限大的区域,每条街道假设坐标是(X,Y),小易当前在(0,0)街道,办公室在(gx,gy)街道上。小易周围有多个出租车打车点,小易赶去办公室有两种选择,一种就是走路去公司,另外一种就是走到一个出租车打车点,然后从打车点的位置坐出租车去公司。每次移动到相邻的街道(横向或者纵向)走路将会花费walkTime时间,打车将花费taxiTime时间。小易需要尽快赶到公司去,现在小易想知道他最快需要花费多少时间去公司。 
  输入描述:
  输入数据包括五行:
  第一行为周围出租车打车点的个数n(1 ≤ n ≤ 50)
  第二行为每个出租车打车点的横坐标tX[i] (-10000 ≤ tX[i] ≤ 10000)
  第三行为每个出租车打车点的纵坐标tY[i] (-10000 ≤ tY[i] ≤ 10000)
  第四行为办公室坐标gx,gy(-10000 ≤ gx,gy ≤ 10000),以空格分隔
  第五行为走路时间walkTime(1 ≤ walkTime ≤ 1000)和taxiTime(1 ≤ taxiTime ≤ 1000),以空格分隔
  输出描述:
  输出一个整数表示,小易最快能赶到办公室的时间
  输入例子:
  2
  -2 -2
  0 -2
  -4 -2
  15 3
  输出例子:
  42

 题目分析和解答:

  这一道题目比较简单,共有两种去到公司的方法,走路或者打车,题意提供的打车点不超过50个,也就是说在最多选择的情况下不会超过51种方法,因此完全可以采用暴力枚举的方法解决这个问题。程序如下。

#include<iostream>
#include<cmath>
#include<vector>
using namespace std;

int main()
{
    int n,gx,gy,wtime,ttime;
    vector<int> tx, ty;
    int min = 0, res = 0;

    cin >> n;
    for (int i = 0; i < n; i++)
    {
        int temp;
        cin >> temp;
        tx.push_back(temp);    
    }
    for (int i = 0; i < n; i++)
    {
        int temp;
        cin >> temp;
        ty.push_back(temp);
    }
    cin >> gx >> gy >> wtime >> ttime;

    // walking value;
    min = (abs(gx) + abs(gy)) * wtime;

    for (int i = 0; i < n; i++)
    {
        res = (abs(tx[i]) + abs(ty[i])) * wtime + (abs(gx - tx[i]) + abs(gy - ty[i])) *ttime;
        if (res < min)
            min = res;
    }
    cout << min;
    return 0;
}

4.【编程题】 调整队形

  时间限制:1秒

  空间限制:32768K

  在幼儿园有n个小朋友排列为一个队伍,从左到右一个挨着一个编号为(0~n-1)。其中有一些是男生,有一些是女生,男生用\'B\'表示,女生用\'G\'表示。小朋友们都很顽皮,当一个男生挨着的是女生的时候就会发生矛盾。作为幼儿园的老师,你需要让男生挨着女生或者女生挨着男生的情况最少。你只能在原队形上进行调整,每次调整只能让相邻的两个小朋友交换位置,现在需要尽快完成队伍调整,你需要计算出最少需要调整多少次可以让上述情况最少。例如:
  GGBBG -> GGBGB -> GGGBB
  这样就使之前的两处男女相邻变为一处相邻,需要调整队形2次 
  输入描述:
  输入数据包括一个长度为n且只包含G和B的字符串.n不超过50.
  输出描述:
  输出一个整数,表示最少需要的调整队伍的次数
  
  输入例子:
  GGBBG

  输出例子:
  2

题目分析和解答:

  这道题目并不复杂,但是一开始想复杂了,看到最小调整队伍的次数的时候,一开始就想到的是动态规划里“字符串相似度”问题解决,但是考虑了之后发现比较难发现最优子结构,然后重新审题,字符串长度最多不超过50,可以知道这不是一个复杂的问题,问题的复杂度也不会高到哪里去。

  因此实际上交换的结果就是女生和女生在一起,男生和男生在一起,只有两种可能:GG……GBB……B,或者BB……BGG……B,实际上最终的交换次数可以如下表计算:

Index

0

1

2

3

4

5

6

 

G

G

B

B

G

G

B

Swap

B

B

B

G

G

G

G

  实际上Boy的交换次数计算为: (2-0)+(3-1)+(6-2) = 8次;

  因此按照思路,计算两种安排可能下的交换次数,取最小值即为答案。

#include <iostream>
#include <string>
using namespace std;

int main()
{
    string list;
    int bnum = 0, gnum = 0;
    int b_index_sum = 0, g_index_sum = 0;
    int b_move = 0, g_move = 0;
    cin >> list;

    for (int i = 0; i < list.length(); i++)
    {
        if (list[i] == \'B\')//boy   if == 一开始写错= 啊低级错误
        {
            b_index_sum += i;
            bnum++;
        }

        if (list[i] == \'G\')//girl
        {
            g_index_sum += i;
            gnum++;
        }
    }
    //boy-move-step
    b_move = b_index_sum - (bnum - 1)*bnum / 2;
    //girl-move-step
    g_move = g_index_sum - (gnum - 1)*gnum / 2;
    cout << ((b_move < g_move) ? b_move : g_move);  //运算符的优先级  注意一下<< 优先级大于三目运算
    return 0;

}

 5.【编程题】魔力手环

  时间限制:1秒

  空间限制:32768K

  小易拥有一个拥有魔力的手环上面有n个数字(构成一个环),当这个魔力手环每次使用魔力的时候就会发生一种奇特的变化:每个数字会变成自己跟后面一个数字的和(最后一个数字的后面一个数字是第一个),一旦某个位置的数字大于等于100就马上对100取模(比如某个位置变为103,就会自动变为3).现在给出这个魔力手环的构成,请你计算出使用k次魔力之后魔力手环的状态。 
  输入描述:
  输入数据包括两行:
  第一行为两个整数n(2 ≤ n ≤ 50)和k(1 ≤ k ≤ 2000000000),以空格分隔
  第二行为魔力手环初始的n个数,以空格分隔。范围都在0至99.
  输出描述:
  输出魔力手环使用k次之后的状态,以空格分隔,行末无空格。

输入例子:
  3 2
  1 2 3
  
 输出例子:
  8 9 7

 题目分析和解答:

   这一道题目如果直接来求解,代码很简单,但是问题就出在 k(1 ≤ k ≤ 2000000000) 上,k的值太大,则很容易就超时超空间,参看了别人的解答,发现这道题目是一道蛮典型的矩阵快速幂模板题,矩阵快速幂用于求解这种大量递推次数的题目,求解的关键也是获得递推矩阵。

  在这道题目中,假设初始状态 T= (x, x, x3…… xn,递推矩阵为A,则第K次后状态为T= AK * T0

  根据题意,递推矩阵为如下类似所示,使用快速幂取模模板求解即可,注意矩阵中0比较多,这一点可以优化一下矩阵乘法

      

#include <iostream>
#include <cstdio>
#include <cstring>
using namespace std;

const int NUM = 51;
const int MOD = 100;

struct mat{
    int n, m;  //行,列
    int data[NUM][NUM];
};

/*
mat mul(mat A, mat B)  //矩阵乘法 这题里需要考虑一下
{    
    mat ret;
    ret.n = A.n;  //行
    ret.m = B.m;  //列

    for (int i = 0; i < A.n; i++)
    {
        for (int j = 0; j < B.m; j++)
        {
            ret.data[i][j] = 0;
            for (int k = 0; k < B.n; k++)  //A.m = B.n
                ret.data[i][j] += (A.data[i][k] * B.data[k][j]);
            if (ret.data[i][j] >= MOD)   //减少一定的取模运算  取模操作耗时
                ret.data[i][j] %= MOD;  
        }
    }
    return ret;
}*/

// 优化代码
mat mul(mat A, mat B)  //矩阵乘法 这题里需要考虑一下
{    
    mat ret;
    ret.n = A.n;  //
    ret.m = B.m;  //
    
    memset(ret.data,0,sizeof(ret.data));

    for (int i = 0; i < A.n; i++)
    {
        for (int k = 0; k < B.n; k++)
        {
            if(A.data[i][k]){
                for (int j = 0; j < B.m; j++) { //A.m = B.n
                    ret.data[i][j] += (A.data[i][k] * B.data[k][j]);
                    if (ret.data[i][j] >= MOD)   
                        ret.data[i][j] %= MOD; 
                }
            }
        }
    }
    return ret;
}

mat mypow(mat A, long long n)
{
    mat ans;
    ans.n = ans.m = A.n;
    if (n == 1) return A;

    //ans 初始化为单位矩阵
    memset(ans.data, 0, sizeof(ans.data));
    for (int i = 0; i < ans.n; i++)
        ans.data[i][i] = 1;

    if (n == 0) return ans;
    
    while (n)
    {
        if (n & 1) ans = mul(ans, A);
        A = mul(A, A);
        n >>= 1;
    }
    return ans;
}

int main()
{
    int n;
    long long k; //k 次操作
    mat base,org,ret;
    memset(org.data, 0, sizeof(org.data));

    cin >> n >> k;  //n个数 k次变化
    for (int i = 0; i < n; i++)
    {
        int d;
        cin >> d;
        org.data[i][0] = d;
    }

    //org为源状态向量
    org.n = n;
    org.m = 1;

    //构建递推矩阵;
    base.n = base.m = n;
    for (int i = 0; i < n; i++)
        for (int j = 0; j < n; j++)
        {
            if (i == j || (i + 1) % n == j)
                base.data[i][j] = 1;
            else
                base.data[i][j] = 0;
        }
    
    base = mypow(base, k);
    ret = mul(base, org);
    for (int i = 0; i < n-1; i++)
    {
        cout << ret.data[i][0] << " ";
    }
    cout << ret.data[n-1][0];

    return 0;
}
6.【编程题】 工作安排

  时间限制:1秒

  空间限制:32768K

  现在有n位工程师和6项工作(编号为0至5),现在给出每个人能够胜任的工作序号表(用一个字符串表示,比如:045,表示某位工程师能够胜任0号,4号,5号工作)。现在需要进行工作安排,每位工程师只能被安排到自己能够胜任的工作当中去,两位工程师不能安排到同一项工作当中去。如果两种工作安排中有一个人被安排在的工作序号不一样就被视为不同的工作安排,现在需要计算出有多少种不同工作安排计划。 
  输入描述:
  输入数据有n+1行:
  第一行为工程师人数n(1 ≤ n ≤ 6)
  接下来的n行,每行一个字符串表示第i(1 ≤ i ≤ n)个人能够胜任的工作(字符串不一定等长的)
  
 输出描述:
  输出一个整数,表示有多少种不同的工作安排方案
·
  输入例子:
  6
  012345
  012345
  012345
  012345
  012345
  012345

  输出例子:

  720
题目分析解析:

   这一道题目看起来就比较适合回溯法。解题代码如下所示:

#include<iostream>
#include<string>
#include<vector>
#include<set>
using namespace std;

vector<string> v;
set<int> ret;
int n;
int tot =0;

void track_back(int cur)
{
    if (cur == n) tot++;  //为一种安排
    else
    {
        string curr = v[cur];
        for (int i = 0; i < curr.length(); i++)
        {
            int s = curr[i] - \'0\';
            if (ret.find(s) == ret.end())
            {
                ret.insert(s);
                track_back(cur + 1);
                ret.erase(s);
            }    
        }
    }
}

int main()
{
    cin >> n;
    for (int i = 0; i < n; i++)
    {
        string temp;
        cin >> temp;
        v.push_back(temp);
    }

    track_back(0);
    cout << tot;

    return 0;
}

7.【编程题】 集合

  时间限制:1秒

  空间限制:32768K

  小易最近在数学课上学习到了集合的概念,集合有三个特征:1.确定性 2.互异性 3.无序性.
  小易的老师给了小易这样一个集合:
  S = { p/q | w ≤ p ≤ x, y ≤ q ≤ z }
  需要根据给定的w,x,y,z,求出集合中一共有多少个元素。小易才学习了集合还解决不了这个复杂的问题,需要你来帮助他。 
  输入描述:
  输入包括一行:
  一共4个整数分别是w(1 ≤ w ≤ x),x(1 ≤ x ≤ 100),y(1 ≤ y ≤ z),z(1 ≤ z ≤ 100).以空格分隔
  
  输出描述:
  输出集合中元素的个数
  输入例子:
  1 10 1 1

  输出例子:
  10

 题目解析:

  这一道题目很简单,就是一个set判重的问题,但是注意一下分数与浮点数的问题。

  看别人的建议说不能直接相除,因为浮点数不精确。但我用了double类型的代码也AC了。所以觉得有点奇怪。还是要尝试搞明白一点。

  建议说建一个结构体来保存分数,主要思想如下所示。

 

 int gcd(int a, int b) {  
    return b == 0 ? a : gcd(b, a%b);  
}  

 int g = gcd(i, j);  
 s.insert(make_pair(i/g, j/g));  
 

 

 

 

#include<iostream>
#include<set>
using namespace std;

int main()
{
    set<double> ret;
    int w, x, y, z;
    cin >> w >> x >> y >> z;  //输入

    for (int i = w; i <= x; i++)
        for (int j = y; j <= z; j++)
        {
            double temp = (double)i / (double)j;
            if (ret.find(temp) == ret.end())
                ret.insert(temp);
        }

    cout<<ret.size();

    return 0;

}

8.[编程题] 奇怪的表达式求值

  时间限制:1秒

  空间限制:32768K

  常规的表达式求值,我们都会根据计算的优先级来计算。比如*/的优先级就高于+-。但是小易所生活的世界的表达式规则很简单,从左往右依次计算即可,而且小易所在的世界没有除法,意味着表达式中没有/,只有(+, - 和 *)。现在给出一个表达式,需要你帮忙计算出小易所在的世界这个表达式的值为多少 
  输入描述:
  输入为一行字符串,即一个表达式。其中运算符只有-,+,*。参与计算的数字只有0~9.
 

以上是关于网易2017 实习生笔试题的主要内容,如果未能解决你的问题,请参考以下文章

实习笔试题,源代码编译

网易2017内推笔试题 合唱团

阿里巴巴2017实习生笔试题

笔试题81. 腾讯2017暑期实习生笔试题

网易2017年校招笔试题 最大的奇约数

算法面试题-今日头条2017客户端工程师实习生笔试题4:或与加