2021网易实习生笔试题(一场计算机视觉、一场数据研发后端)

计算机视觉算法岗

网易的笔试貌似是有好多题随机给四道做,看评论大家的题并不完全一样,我找到的四道题如下:

题目一 牛牛的等差数列

长度为n的数组a,找到一个最大的正整数d,使得对于所有i,a[i+1]-a[i]是d的倍数,即a[i+1]-a[i]=d×k(k≥1)
输入:
第一行一个正整数n,2<=n<=2*10^5
第二行n个正整数a[i],1<=a[i]<=10^18
输出:
若d不存在输出-1,否则输出最大的d。
样例1:
输入
4
1 3 7 15
输出
2
样例2
输入
3
5 5 5
输出
-1

分析

d和k都必须是正整数,那么先把所有差值求出来排个序,如果最小值小于1那么必然不存在d,如果大于1则求所有差值的gcd即可。复杂度O(n)。

代码

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

int n;
long long a[200010];
long long cha[200010];
long long gcd(long long a, long long b)
{
    return b ? gcd(b, a%b) : a;
}
int main()
{
    cin  >> n;
    for (int i = 0; i < n; i++)
    {
        cin >> a[i];
        if (i)
            cha[i-1] = a[i] - a[i-1];
    }
    sort(cha, cha + n-1);
    if (cha[0] < 1) cout << -1 << endl;
    else {
        long long ans = cha[0];
        for (int i = 1; i < n-1; i++)
            ans = gcd(ans, cha[i]);
        cout << ans << endl;
    }
    return 0;
}

题目二

最近牛牛约了几个同学一起去看电影,买了恰好数量的横排连坐票,但是有些同学关系很差,不愿意坐一起,牛牛想知道所有可能安排座位的情况。
输入:
第一行为两个数字n,k,代表总人数n和k个很差的关系。
接下来k行,每行两个数字x和y表示同学x与y之间关系很差,不能挨着坐,所有人编号为1到n。
1<=n<=9,0<=k<=100,保证x != y,可能有重复对。
输出:
每行输出一组解,按照字典序输出,相邻编号用空格隔开。
数据保证有解。
样例1:
输入
3 2
1 2
2 1
输出
1 3 2
2 3 1
样例2:
输入
3 0
1 2 3
1 3 2
2 1 3
2 3 1
3 1 2
3 2 1

分析

数据量非常小,直接用深搜回溯即可,这样也能保证字典序,具体看代码。

代码

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

int n, k;
vector<vector<int> > v;//存放关系差的关系对
int vis[10];//标记已经排过位置的人
//index表示前一个位置坐的是谁, cnt用来计数当前已经排好了几个位置,now存放结果
void dfs(int index, int cnt, vector<int>& now)
{
    if (cnt == n){
        for (int i = 0; i < now.size(); i++){
            if (i) cout << " ";
            cout << now[i];
        }
        cout << endl;
        return;
    }
    for (int i = 1; i <= n; i++) if (!vis[i]){
        int flag = 1;
        for (int j = 0; j < v[index].size(); j++) if (v[index][j] == i) {
            flag = 0;
            break;
        }
        if (flag){
            vis[i] = 1;
            now.push_back(i);
            dfs(i, cnt+1, now);
            now.pop_back();//回溯
            vis[i] = 0;//回溯
        }
    }
}
int main()
{
    cin >> n >> k;
    v.resize(n+1);
    int x, y;
    for (int i = 0; i < k; i++){
        cin >> x >> y;
        v[x].push_back(y);
        v[y].push_back(x);
    }
    for (int i = 1; i <= n; i++){//以i为头
        memset(vis, 0, sizeof vis);//标记排过位置的人
        vis[i] = 1;
        vector<int> now(1, i);
        dfs(i, 1, now);
    }
    return 0;
}

题目三

牛牛喜欢玩魔塔,魔塔里有n只怪,每只怪有两个属性,破防能力和伤害,当勇士挑战一只怪时,
1.勇士的防御力大于其破防能力,勇士不受伤害;
2.若小于等于,则受到等于怪伤害值的伤害;
初始防御力为D,勇者可以自行决定打哪个怪(任意顺序),每次打完一只怪防御力加1,那么勇者打完所有n只怪最少要承受多少伤害?
输入:
第一行为n和D,1<=n<=5000,1<=D<=5000
第二行有n个正整数Ai,表示第i号怪物的破防能力
第三行有n个正整数Bi,表示第i号怪物的伤害。
输出:
一个整数表示最少承受伤害。
样例1
输入
3 50
100 50 51
1000 1000 1000
输出
1000
解释:只要先抗住第一只怪,受1000伤害,防御力变为51,然后打第二只不受伤害,防御力变为52,再打第三只。

分析

不太好做的题,首先感觉贪心可以搞,先去面对破防能力低的怪,但是如果破防能力低的怪也打不过,且伤害比破防能力高的怪还高,就很难受,比如样例,如果先打第二只怪其实受伤害更多。所以我选择把数据做两份,一份按照破防能力排序,一份按照伤害值排序,当防御力小于等于当前怪物(破防能力最低的)的破防能力时,说明当前防御力打谁都打不过,那就找伤害值最低的怪来打。
这里又出现了一个问题,直接举例来看
2 50
50 60
1 100
现在两只怪都打不过,那么选择伤害更低的来打,选了(50 1)的这只,这样最后要受101点伤害,如果直接选择(60,100),打完以后防御力变为51,最后只受100伤害。
所以要先把一定打不过的先打了,什么怪一定打不过?破防能力大于等于D+n-1的!!!所以先把这些怪打了,假如有m只,那么现在防御力变成了D+m。
然后把剩下的怪按照上面贪心的思路,存两份,然后不断打怪即可。
我的代码写的时候图方便,把破防能力小于D的也先去掉了。
这题n并不大,复杂度O(n^2)可以接受。

代码

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

struct node
{
    int index;//存放怪物编号
    int a, b;//破防, 伤害
    node(int i = 0, int aa = 0, int bb = 0) : index(i), a(aa), b(bb){}
};
node defend[5010], attack[5010];
bool cmp1(const node & x, const node & y)//按破防从小到大
{
    if (x.a == y.a) return x.b < y.b;
    return x.a < y.a;
}
bool cmp2(const node & x, const node & y)//按伤害从小到大
{
    if (x.b == y.b) return x.a > y.a;
    return x.b < y.b;
}
int main()
{
    int N, D;
    cin >> N >> D;
    int aa[N], bb[N];
    for (int i = 0; i < N; i++) cin >> aa[i];
    for (int i = 0; i < N; i++) cin >> bb[i];
    int n = 0, sum = 0;
    int d = D;
    for (int i = 0; i < N; i++)
        if (aa[i] >= D+N-1){
                d++;
                sum += bb[i];
        }
        else if (aa[i] >= D){
            defend[n] = node(n, aa[i], bb[i]);
            attack[n] = node(n, aa[i], bb[i]);
            n++;
        }
        else d++;
    sort(defend, defend+n, cmp1);
    sort(attack, attack+n, cmp2);
    int vis[n];//标记已经打过的怪编号
    memset(vis, 0, sizeof vis);
    int cnt = n;//还剩几只怪
    int i = 0, j = 0;
    while (cnt){
        for (; i < n; i++) if (!vis[defend[i].index]){
            if (defend[i].a < d){
                vis[defend[i].index] = 1;
                cnt--;
                d++;
            }
            else break;//如果这只打不过,说明现在所有怪都打不过
        }
        //挑那只伤害值最低的
        for (; j < n; j ++) if (!vis[attack[j].index]){
            vis[attack[j].index] = 1;
            cnt--;
            d++;
            sum += attack[j].b;
            break;
        }
    }
    cout << sum << endl;
    return 0;
}

题目四 穿越激光

有一个n*m的网格,这个网格内有一些障碍物,同时也有一些激光,激光发出的光是一条直线,从该激光发射器到障碍物或其他激光发射器停止,现在小强想从一个点走到另一个点,每一步小强走到周围8个联通的点,但是要求走的路径中相邻两个格子最多只能有一个是有激光的,想知道这个路径中最少有几个格子是被激光照射的,若无法走到输出-1。
输入
第一行一个T,表示T组测试数据;
对于每一组测试数据,第一行两个数n和m表示网格大小。
第二行4个整数,x1,y1,x2,y2表示起点和终点。
第三行一个整数k,表示k个障碍物和激光
接下来k行,每一行二个整数一个字符x,y,op,表示(x,y)处的信息
op为u代表格子为激光且向上(UP)照射
op为d代表格子为激光且向下(DOWN)照射
op为l代表格子为激光且向左(LEFT)照射
op为r代表格子为激光且向右(RIGHT)照射
op为B表示障碍物,无法通过。(题目没说激光发射器位置是否可以通过,样例只能看出该位置也是有激光的)
1<=T<=100
1<=n,m<=20
输出
每组测试数据输出对应的结果。
样例:
输入
2
3 3
1 1 3 3
2
2 1 r
2 3 B
3 2
1 2 3 1
2
2 1 B
2 2 B
输出
1
-1
灵魂画师
这题我写了半天,地图楞没建好,我觉得建好图dfs应该就能做了,但是建图实在太难受写不下去了。以后心情好的时候再写。笔试的时候遇到,怕也不一定有时间来写。

数据研发(后端)笔试题

题目一

某游戏中有一个老用户带新用户的玩法,老用户跟新用户需要两两组队为n个队伍,现在,我们根据用户画像(这么真实的?),已经计算出每个新用户对所有老用户的组队偏好排序,以及每个老用户对所有新用户的组队偏好排序,请你实现一个匹配算法,要求:
1.根据新用户对老用户的偏好排序去匹配老用户;
2.如果被匹配过的老用户已有队友,根据老用户对新用户的偏好排序,为该老用户选择排在前面的新用户。
输入:
第一行一个n,表示新老用户总数,新老用户编号均为1到n;
接下来n行,第一个数字为新用户编号,接下来n个数字为该新用户对所有老用户的偏好排序;
再接下来n行,第一个数字为老用户编号,接下来n个数字为该老用户对所有新用户的偏好排序;
输出:
n行,每一行为队伍的新用户和老用户编号,空格隔开,按照新用户的编号从低至高排列。
样例:
输入
3
1 2 3 1
2 1 3 2
3 1 2 3
1 1 2 3
2 1 2 3
3 1 2 3
输出
1 2
2 1
3 3

分析

这题实际上是一个匹配问题,我们模拟一下这个情况,如果现在有两个新用户选择了同一个老用户,那么就要按照这两个新用户在该老用户那里的优先级进行比较,然后配对,没有配成的那个人就选择他优先级里下一个老用户继续进行配对,不断进行这个过程,直到所有人都配对成功,所以仔细一想这就是个婚姻匹配问题,我借鉴了一下这篇博客的代码(好吧我是抄了一遍改了改),具体看代码:

代码

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

const int N = 1010;
int man[N][N], woman[N][N], match[N];//man[i][j]表示新人i心中排在第j位的老用户编号,woman[i][j]存放老人i心中第j位的新用户编号
//match存放匹配答案
int wm[N][N], choose[N], manIndex[N];
//wm[i][j]表示新人j在老人i心中的位置(优先级)
//choose[i]表示老人i现在选择的新人编号
//manIndex[i]表示新人i现在需要跟他心中第manIndex[i]个人进行匹配,前面man[i][1]到man[i][manIndex[i]-1]都被别人抢了
int n;

int main()
{
    cin >> n;
    int xin, lao;
    for (int i = 1; i <= n; i++){
        cin >> xin;
        for (int j = 1; j <= n; j++){
            cin >> lao;
            man[xin][j] = lao;
        }
    }
    for (int i = 1; i <= n; i++){
        cin >> lao;
        for (int j = 1; j <= n; j++){
            cin >> xin;
            woman[lao][j] = xin;
        }
    }
    //初始化
    for (int i = 1; i <= n; i++){
        match[i] = -1;
        choose[i] = -1;
        manIndex[i] = 1;//从第一个人开始
        for (int j = 1; j <= n; j++)
            wm[i][woman[i][j]] = j;
    }
    bool bSingle = false;
    while (!bSingle){
        bSingle = true;
        for (int i = 1; i <= n; i++){
            if (match[i] != -1)//如果所有i都有匹配了,结束
                    continue;
            bSingle = false;
            int j = manIndex[i]++;
            int w = man[i][j];//尝试匹配i和w
            int m = choose[w];
            //看一下w现在有没有配对的人,如果没有或者那个匹配的人的优先级不如i
            if (m == -1 || wm[w][i] < wm[w][m]){
                match[i] = w;
                choose[w] = i;
                if (m != -1)
                    match[m] = -1;//原来配上的取消掉
            }
        }
    }
    for (int i = 1; i <= n; i++)
        cout << i << " " << match[i] << endl;
    return 0;
}

题目二

《荒野行动》是网易游戏出品的一款战术竞技游戏,开局物资的获取对于玩家后续游戏发展影响很大,因此玩家都会在降落之后进行一波疯狂的物资搜集,而这些物资散落在地图的各个角落,受限于地图上建筑的阻挡,有些物资点是玩家无法到达的,假设我们用n*n的矩阵来表示地图,其中字符"."表示道路,字符“#”表示建筑,阿拉伯数字1~9表示物资,数值是物资的重量。玩家降落后有一个初始位置(x,y)其中0<=x,y<n,保证不会降落在建筑上,此后玩家可以进行上下左右四个方向移动,且不能超出边界,当到达物资地时自动将物资装入背包,假设背包无限大,请计算一下玩家可以搜集到的物资总重量。
输入
第一行一个整数n,6<=n<=3000,表示地图大小;
接下来n行,每行n个字符,表示地图;
接下来两个整数x,y,表示初始位置。
输出
搜集完毕后背包的重量
样例:
输入
6
…1.#.
2##3#8
.#.##.
.#6#9.
4###…
…7…5
0 0
输出
39

分析

题目蛮长的,就是一个搜索题,不要求找路径的话用BFS写会好很多。具体看代码。

代码

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

int n, x, y;
int vis[3010][3010];
int dx[4] = {1, -1, 0, 0};
int dy[4] = {0, 0, 1, -1};
char mp[3010][3010];

int main()
{
    cin >> n;
    for (int i = 0; i < n; i++)
        cin >> mp[i];
    cin >> x >> y;
    queue<pair<int, int>> que;
    que.push(make_pair(x, y));
    vis[x][y] = 1;
    int ans = 0;
    if (mp[x][y]>='1' && mp[x][y]<='9') ans += mp[x][y]-'0';
    while (!que.empty()){
        pair<int, int> now = que.front();
        que.pop();
        for (int i = 0; i < 4; i++){
            int nx = dx[i] + now.first;
            int ny = dy[i] + now.second;
            if (nx<0 || nx>=n || ny<0 || ny>=n) continue;
            if (mp[nx][ny]=='#' || vis[nx][ny]) continue;
            que.push(make_pair(nx, ny));
            vis[nx][ny] = 1;
            if (mp[nx][ny]>='1' && mp[nx][ny]<='9')
                ans += mp[nx][ny]-'0';
        }
    }
    cout << ans << endl;
    return 0;
}

题目三 SQL

中国电子游戏发展至今,经历了单机游戏、MMO端游、网页游戏、移动游戏等多种游戏类型,已经积累了大量的游戏忠实用户。随着大数据的迅速发展和普及,通过数据分析、挖掘来对游戏用户做研究,提升用户体验,变得越来越重要,下面是一份用户关注数据,表名src_tieba_user,表结构如下:

字段名类型说明
tbanamevarchar贴吧名
usernamevarchar用户名

请仔细阅读并理解SQL,输出该SQL的执行结果。

SELECT
	a.tbaname,
	b.tbaname,
	COUNT(DISTINCT a.username) AS num
FROM
	src_tieba_user a
INNER JOIN
	src_tieba_user b
WHERE
	a.username = b.username AND a.tbaname != b.tbaname
GROUP BY
	a.tbaname, b.tbaname
ORDER BY
	a.tbaname, b.tbaname
HAVING
	num>2;

输入
第一行一个整数n,1<=n<=3000,表示表中数据的记录数
接下来n行,每行两个字符串tbaname,username,字符串长度不超过10个字符,两个字符串空格分隔,表示数据表中的一条记录。
输出
结合输入数据,输出以上SQL的执行结果,每行数据的字段用空格分隔。
样例
输入
8
ddd Kate
ccc Kate
ccc Beal
eee Tom
ddd Beal
bbb Kate
ddd Tom
ccc Tom
输出
ccc ddd 3
ddd ccc 3

我还没学过SQL,之后再写吧。

  • 1
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值