今日头条2018安卓实习笔试题

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/A657997301/article/details/79848630

好多天前做的头条,今日补上博客。


第一题

题意

在n个元素的数组中,找到差值为k的数字对去重后的个数。

输入描述:
第一行包含两个正整数,n和k,n表示数字个数,k表示差值。
第二行,n个正整数

输出描述:
差值为k的数字对去重后的个数

输入

5 2
1 5 3 4 2
5 0
1 1 1 2 2

输出

3
2

说明

第一个样例,数字对分别为(1,3),(5,3),(4,2)
第二个样例,数字对分别为(1,1),(2,2)

思路

  分两种情况讨论:
  1、k大于0,此时的重复对肯定不是(1,1),(2,2),所以对所有的数排序,然后去掉重复的数字。遍历所有数字,对每个数字都再遍历其之后的数字,如果有满足差值为k的数字,或者遍历到差值已经大于k的数字,都不用再遍历其之后的数字(节省时间)。
  2、k为0,此时的重复对便形如(1,1),(2,2),所以不能再去重了。仍然对所有的数排序,然后遍历所有数字,如果找到某一个数的后一个数刚好与其相等,说明找到题意所求,记得让遍历的索引跳过这个数之后的所有与这个数相等的元素。

代码

#include <vector>
#include <algorithm>
#include <cstdio>

using namespace std;

int main(void) {
    int num, key, i, j;
    scanf("%d%d", &num, &key);

    vector<int> a;
    vector<int>::iterator it1;
    vector<int>::iterator it2;
    // 记录答案 
    int count = 0;

    for(i = 0; i < num; i++) {
        scanf("%d", &j);
        a.push_back(j);
    }
    sort(a.begin(), a.end());

    // key不为0,则大胆去重后双重循环找 
    if (key != 0) {
        a.erase(unique(a.begin(), a.end()), a.end());
        for(it1 = a.begin(); it1 != a.end(); it1++) {
            for(it2 = it1 + 1; it2 != a.end(); it2++) {
                if(*it2 - *it1 == key) {
                    // 找到立即break节省时间 
                    count++;
                    break;
                }
                if(*it2 - *it1 > key) {
                    // 如果超过key,后面也不可能有了,break 
                    break;
                }
            }
        }
    } else {
        // key为0,则一重循环找即可。 
        for(it1 = a.begin(); it1 != a.end(); it1++) {
            it2 = it1 + 1;
            if (*it2 == *it1) {
                count++;
                while (it1 != a.end() && *it1 == *it2) {
                    // 跳过后面相等的数字 
                    it1++;
                }
                it1--;
            }
        }
    }
    printf("%d\n", count);
}

代码通过样例100%


第二题

题意

定义两个字符串变量:s和m,再定义两种操作
第一种操作:
  m = s;
  s = s + s;
第二种操作:
  s = s + m;
假设s,m初始化如下:
  s = "a";
  m = s;
求最小的操作步骤数,可以将s拼接到长度等于n。

输入描述:
一个整数n,表明我们需要得到s字符串长度
对于100%的数据,0

输入

6

输出

3

说明

两次第一种操作+一次第二种操作

思路

  每次操作都是同样的两手选择,这种重复性可以用递归去模拟。这种类似枚举的过程,能让我们把所有能让s拼接到长度为n的操作都模拟出来,维护一个全局变量,每次模拟出就取最小值即可。长度为n是一种递归结束条件,另一种递归结束条件是长度大于n,因为之后怎么操作也会大于n。

代码

#include <iostream>  
#include <cstdio>  
#include <string>  
using namespace std;  
int n;
int res;

void cal(string s, string m, int sum) {
    if (s.length() > n) return;
    if (s.length() == n) {
        res = min(res, sum);
        return;
    }
    // 第一种操作
    string newm = s;
    string news1 = s + s;
    cal(news1, newm, sum+1);
    // 第二种操作 
    string news2 = s + m;
    cal(news2, m, sum+1);
}

int main(void) {
    cin >> n;
    // res最大值为n-1,因为可以用n-1次第二种操作 
    res = n-1;
    string s = "a";
    string m = s;
    // 第三个参数代表操作数 
    cal(s, m, 0);
    cout << res << endl; 
}

第三题——纸牌游戏

题意

读取一个表达式,输出以下格式表示的计算结果。
如下是”1234567890”
这里写图片描述

输入描述:
第一行为一个正整数n
接下来n行每一行为一个表达式

输出描述:
输出字符’6’拼成的计算结果

输入

2
6+6
6*6

输出

这里写图片描述

思路

  首先解决计算表达式的问题。样例只有加减乘(然而题意没说,所以真的很坑)。加减乘的表达式可以理解为A*a+B*b+C*c,这里的大写字母都是权值(系数),小写字母是真实数字。比如:
  1+9则可以当做0+1*1+1*9(此时两个权值都为1)。
  6-5+2*24则可以当做0+1*6-1*5+2*24(此时三个权值分别为1,-1,2)。
  一个表达式的计算结果从0开始,不断“+权值*数字”。理解到这就知道怎么计算表达式了,维护三个变量,一个cur为当前数字的值,一个prd为当前数字的权值,一个sum为当前数字前的表达式的总和。于是,每识别完一个数字就sum+=prd*cur即可。
  现在有了表达式计算结果,再不断取各位上的数字出来。维护一个大数字的三维数组,根据取出的位数,取出对应行列位置的字符串,循环五次绘制整个数字(因为一整个大数字有五行)

代码

#include <iostream>  
#include <cstdio>  
#include <cstring>
#include <vector>
#include <algorithm>

using namespace std;

char big[5][10][6] = {
    {"66666", "....6", "66666", "66666", "6...6", "66666", "66666", "66666", "66666", "66666"},  
    {"6...6", "....6", "....6", "....6", "6...6", "6....", "6....", "....6", "6...6", "6...6"},  
    {"6...6", "....6", "66666", "66666", "66666", "66666", "66666", "....6", "66666", "66666"},  
    {"6...6", "....6", "6....", "....6", "....6", "....6", "6...6", "....6", "6...6", "....6"},  
    {"66666", "....6", "66666", "66666", "....6", "66666", "66666", "....6", "66666", "66666"}  
};
long long cal(char s[]) {
    int n = strlen(s);
    long long sum = 0, cur = 0, prd = 1;
    for (int i = 0; i < n; ++i) {
        if (s[i] >= '0' && s[i] <= '9') {
            cur = cur*10+s[i]-'0';
        } else if(s[i] == '+') {
            sum += prd*cur;
            cur = 0;
            prd = 1;
        } else if (s[i] == '-') {
            sum += prd*cur;
            cur = 0;
            prd = -1;
        } else if (s[i] == '*') {
            prd *= cur;
            cur = 0;
        }
    }
    sum += prd*cur;
    return s;
}
int main(void) {
    int n;
    scanf("%d", &n);
    while (n--) {
        char s[100];
        scanf("%s", s);

        // 计算表达式结果
        long long ans = cal(s);

        // 取各位上的数装进vector
        vector<int> v;
        long long tmp = ans;
        while (tmp) {
            int wei = tmp % 10;
            v.push_back(wei);
            tmp /= 10;
        }
        reverse(v.begin(), v.end());

        // 记得还有结果为0的情况
        if (v.empty()) {
            v.push_back(0);
        }

        for (int i = 0; i < 5; ++i) {
            // i代表当前要画第几行,因为大数字总共有五行
            for (int j = 0; j < v.size(); ++j) {
                // 判断要画哪个数字
                int num = v[j];
                printf("%s%s", big[i][num], j+1==v.size() ? "\n" : "..");
            }
        }
    }
    return 0;  
}

第四题

题意

给一个包含n个整数元素的集合a,一个包含m个整数元素的集合b。
定义magic操作为,从一个集合中取出一个元素,放到另一个集合里,且操作过后每个集合的平均值都大于操作前。
注意以下两点:
①不可以把一个集合的元素取空,这样就没有平均值了
②值为x的元素从集合b取出放入集合a,但集合a中已经有值为x的元素,则a的平均值不变(因为集合元素不会重复),b的平均值可能会改变(因为x被取出了)
问最多可以进行多少次的magic操作?

输入描述:
第一行为两个整数n,m
第二行n个整数,表示集合a中的元素
第三行m个整数,表示集合b中的元素
对于30%的数据,最终结果<=1
对于70%的数据,输入中的a,b集合元素完全没有重复,即|a|+|b|=|aUb|
对于100%的数据,1<n,m<100,000,0<a[i],b[i]<100,000,000,集合a中元素互不相同,集合b中元素互不相同。

输出描述
输出一个整数,表示最多可以进行的操作次数。

输入

3 5
1 2 5
2 3 4 5 6

输出

2

说明

从集合b中取出3,4元素放入集合a中

思路

  假设A的均值比B的均值更大,操作次数要尽可能地多,那么最贪心的做法是从A里取一个最小值元素丢到B里了,这样A的均值涨的比较快,可以取的就更多了,而B的平均值涨的比较慢,可以丢的就更多了。
  然后一直从A里取越来越大的值(不超过A的均值,不然A的均值没法涨)丢到B里。注意,这个丢的过程中A、B的平均值都会涨。而且A也不是所有元素都可以丢到B里的,只能从A里取出现次数等于1的数(不然A的均值不涨),而且这个数在B里不能出现过(不然B的均值不涨)。
  综上所有条件,A取越来越大的元素,所以要对A进行排序。只能取出现次数为1的数,所以要对A去重。因此考虑用set容器。

代码

#include <bits/stdc++.h>

using namespace std;

const long double eps = 1e-14;
int cmp(long double a, long double b) {
    if (fabs(a-b) <= eps) return 0;
    return a > b ? 1 : -1;
}

long double chu(long long k, int m) {
    return (long double)k/m;
}

int main(void) {
    int n, m;
    scanf("%d%d", &n, &m);

    set<int> numA, numB;
    long long sumA = 0, sumB = 0;
    for (int i = 0; i < n; ++i) {
        int tmp;
        scanf("%d", &tmp);
        numA.insert(tmp);
        sumA += tmp;
    }
    for (int i = 0; i < m; ++i) {
        int tmp;
        scanf("%d", &tmp);
        numB.insert(tmp);
        sumB += tmp;
    }
    // 保证numA的均值是大于numB的均值的,如果不是这样则交换AB 
    if (cmp(chu(sumA, n), chu(sumB, m)) == -1) {
        swap(sumA, sumB);
        swap(n, m);
        numA.swap(numB);
    }
    int res = 0;
    // 按小到大顺序,不断取A的元素 
    for (set<int>::iterator it = numA.begin(); it != numA.end(); it++) {
        int k = *it;
        // A是有序的,如果当前元素大于等于A的均值,则不用再取了 
        if (cmp(k, chu(sumA, n)) >= 0)
            break;
        if (!numB.count(k) && cmp(k, chu(sumB, m)) > 0) {
            // 放到B的条件是要大于B的均值并且B没有这个数 
            ++res;
            sumB += k;
            ++m;
            sumA -= k;
            --n;
        }
    }
    printf("%d\n", res);
    return 0;
}

第五题

题意

小T最近迷上了一款跳板小游戏。
已知空中有N个高度互不相同的跳板,小T刚开始在高度为0的地方,每次跳跃可以选择与自己当前高度绝对值差小于等于H的跳板,跳跃过后到达以跳板为轴的镜像位置,问小T在最多跳K次的情况下最高能跳多高?(任意时刻,高度不能为负)

输入描述:
三个整数,N,K,H
以下N行,每行一个整数T[i],表示第i个跳板的离地高度。

输出描述:
一个整数,表示最高能跳到的高度

输入

3 3 2
1
3
6

输出

8

说明

第一次跳跃,选择高度为1的跳板,结束后到达高度为2的地方。0+(1-0)*2=2,所以结束后到高度为2的地方(注意是以跳板为轴的镜像位置)
计算公式:结束高度 = 初始高度 + (所选跳板高度-初始高度)*2

思路

  初始位置是0,先把所有的板的位置记录下来,然后用BFS,将位置小于(0+可跳高度H)的所有跳板都跳个遍,记录所有能到达的位置,然后对这些到达的位置再重复同样的操作(所有记录的位置都会跟保存最终结果的变量比较大小),这期间再维护一个变量记录跳到该位置时耗费的跳的次数(用来与K比较,而又要记录位置又要记录跳的次数,可以考虑用pair),一直到跳的次数到达K或者没跳板可以跳了再终止BFS。
  然而这样有个问题,比如板有5,9,11,13,H为5,跳跃的次数无穷大,那么不断往上跳可以从
  0→(通过5)→10→(通过13)→16
  然而其实最远的是
  0→(通过5)→10→(通过9)→8→(通过13)→18
  也就是,最完美的情况是(最高跳板的位置+H)。而有可能因为我们离最高跳板比较近,导致能跳的距离小于H,就达不到最远距离了。这时候如果能往后跳一下,再跳最高的那块跳板就能跳得更远了,因此BFS的时候不仅要将往上的所有能跳的跳板都跳了,还要往下将所有能跳的跳板也跳了

代码

#include <bits/stdc++.h>

using namespace std;

bool Postion[250000];
bool Springboard[250000];
int main(void) {
    int n, k, h;
    scanf("%d%d%d", &n, &k, &h);
    for (int i = 0; i < n; ++i) {
        int tmp;
        scanf("%d", &tmp);
        Springboard[tmp] = true;
    }

    // BFS,pair第一个表位置,第二个表已跳跃次数
    queue<pair<int, int> > q;
    q.push(make_pair(0, 0));

    int res = 0;
    while (!q.empty()) {
        pair<int, int> p = q.front();
        q.pop();
        // BFS,次数超过k,后面的也肯定超过K,不用看了 
        if(p.second>k) break;

        res = max(res, p.first);
        for (int i = 1; i <= h; ++i) {
            if (Springboard[p.first + i] && !Postion[p.first+2*i]) {
                Postion[p.first+2*i] = true;
                q.push(make_pair(p.first+2*i, p.second+1));
            }
            if (p.first-2*i > 0 && Springboard[p.first-i] && !Postion[p.first-2*i]) {
                Postion[p.first-2*i] = true;
                q.push(make_pair(p.first-2*i, p.second+1));  
            }
        }
    }
    printf("%d\n", res);
    return 0;
}

总结

不得不说今日头条一下五道编程题还是很恐怖的,比其他厂的笔试题要难很多。

猜你喜欢

转载自blog.csdn.net/A657997301/article/details/79848630