【校招笔试】2019春招——字节跳动C++后端研发,第一轮线上笔试

背景交代

3月8日的时候百无聊赖投了字节跳动的C++开发岗位,因为是大厂,自己也没有什么特殊的能力与经历,本以为会杳无音讯。

3月13日收到短信——笔试时间通知。

3月16日(也就是今天)早上10点进行2个小时的线上笔试。


准备情况:

10点开始,9点起床,打开链接是牛客网上进行笔试,俺就是裸考。。。光脚的不怕穿鞋的。。。

纯粹的算法题,一共是4题两小时,线上摄像头监控。

早有耳闻字节跳动是一家面试算法狂魔的公司,自己已经有2年多没有捡回算法了,已做好凉凉的准备。

看了一下前两道题目,可能第一轮笔试只是简单的筛选吧,居然是很简单基础的题目。



笔试内容:

完整的题目我没有拷贝下来(没有细研究过牛客网的监控是否会进行屏幕捕捉),就没有对题目进行截图了,我靠记忆说一下大意吧。


第一题

 

 

题面:有1,4,16,64面值的硬币,问小明用1024面值的纸币去买了N块钱的东西,找零的时候最少的硬币数目是多少?

其中N还挺大,1e5

思路:我当时懵了一下,是贪心还是动归啊,不愧是字节跳动,第一题就是动...欸?还有1块的硬币,好那就是快乐的贪心算法啦!于是手写了20分钟的贪心,交了一发就AC了。。。

具体贪心思路就是:硬币既然最少,当然是要先换成面值大一点的硬币,换不了了再换面值校的硬币咯——先换64再换16一直换换到1。换多少枚就是整数的除法运算了呗

难度:签到题

代码如下

#include<bits/stdc++.h>
#include<iostream>
using namespace std;
#define inf 999
#define INF 0x3f3f3f3f

int n,sum=0;
void sub(int k)
{
    int m=n/k;
    sum+=m;
    n-=k*m;
}

int main()
{
    cin>>n;
    n=1024-n;//用的是1024纸币买了n元,所以要找1024-n
    sub(64);
    sub(16);
    sub(4);
    sub(1);
    cout<<sum<<endl;
    return 0;
}

 

第二题
 

题面:王大锤拿到一个字符串,如果有满足条件一:AAA形状连续重复的子串,就把它变成AA;如果有满足条件二:AABB形状连续重复的子串,就把它变成AAB。其中,条件一优先于条件二。

思路:这题就不解释了,如题所示,就是按照题目模拟实现一边就好。怕后面的题目费时间,就直接按题目模拟着写了,写的不是很好看,粗暴无比。我还花几分钟的时间,琢磨了一下string内嵌的成员函数功能是什么。。。

难度:签到题

代码如下

#include<bits/stdc++.h>
#include<iostream>
using namespace std;
#define inf 999
#define INF 0x3f3f3f3f

int n;
string s;

int main()
{
    cin>>n;
    int i,j;
    for(i=0;i<n;i++)
    {
        cin>>s;
        //int len=s.length();
        if(s.length()>=3)//条件A
        {
            char a=s.at(0);
            char b=s.at(1);
            for(j=2;j<s.length();j++)
            {
                if(a==b&&b==s.at(j))
                {
                    s.erase(j,1);
                    j--;//原地不动
                }
                else
                {
                    a=b;b=s.at(j);//next
                }
            }
        }
        if(s.length()>=4)//条件B
        {
            char a=s.at(0);
            char b=s.at(1);
            char c=s.at(2);
            for(j=3;j<s.length();j++)
            {
                if(a==b&&c==s.at(j))
                {
                    s.erase(j,1);
                    j--;//原地不动
                }
                else
                {
                    a=b;b=c;c=s.at(j);//next
                }
            }
        }

        cout<<s<<endl;
    }
    return 0;
}

 

第三题
 

题面:有N个人站成一个圈,第N个人的下一个是第1个人。每个人有自己的对应分数,然后我们要给他们送礼物。送礼物的规则是:第一,如果这个人的分数比他左右的某人要高,那这个人的礼物数量也必须要比比他低的左边那个人或者右边那个人的礼物数量要多;第二,每个人至少要有一个礼物!

问:至少需要多少份礼物?

其中,N是1e5的大小!

思路:写道这里的时候还有1个西奥是20分钟左右的时间,我总觉得字节跳动的笔试没有难的在我意料之中,很不科学。这道题题面很短,感觉好像很简单。

先感觉好像不能排序,都1e5了,晃晃脑袋排序是NlogN哦,是可以的。

感觉就是按照分数排序呗,从分数最低的人开始,给比他分数高的邻居多+1个礼物呗。但是写代码的时候,脑子瓦特了没这么写,还用构建了整个以得分大小为比较内容的拓扑图,然后用拓扑排序给每条路径+1做......但是前面那个思路和拓扑也是一样的。感觉自己好蠢。。。这题先写的贪心,后来脑子瓦特了,重写了拓扑,写了1小时我晕。?

难度:本来以为是一星拓扑排序题,现在看来是贪心签到水题。。。

我的拓扑排序的代码如下

#include<bits/stdc++.h>
#include<iostream>
using namespace std;
#define inf 100009
#define INF 0x3f3f3f3f

int n;
int sum;
int grade[inf];
int award[inf];
int in[inf];
vector<int>e[inf];

void dfs(int i,int value)
{
    award[i]=max(award[i],value);
    int len=e[i].size();
    for(int j=0;j<len;j++)
    {
        int u=e[i][j];
        dfs(u,value+1);
    }
}

int main()
{
    int T;
    int i,j;
    cin>>T;
    while(T--)
    {
        cin>>n;
        sum=0;
        memset(award,0,sizeof award);
        memset(in,0,sizeof in);
        for(i=0;i<n;i++)e[i].clear();

        for(i=0;i<n;i++)cin>>grade[i];//
        for(i=0;i<n;i++)//建图,小的指向大的
        {
            int right=i+1==n?0:i+1;//这里要注意的是等于n为0,我写成大于n找了很久的bug
            if(grade[i]>grade[right])
            {
                e[right].push_back(i);
                in[i]++;//入度
            }
            else if(grade[right]>grade[i])
            {
                e[i].push_back(right);
                in[right]++;
            }
        }
        for(i=0;i<n;i++)//遍历
            if(!in[i])
            dfs(i,0);

        for(i=0;i<n;i++)
            sum+=award[i];//出结果

        for(i=0;i<n;i++)
            if(!award[i])
            {
                sum+=n;//不可谓0
                break;
            }
        cout<<sum<<endl;

    }
    return 0;
}

 

第四题
 

题面:有N条绳子,绳子的各个长度会告诉你。你可以把它们剪断变成好几条,但是你不能把他们缝合起来,只能切不能拼。现在我告诉你我要得到M条一样长的绳子,请问你最长能得到多长的M条一毛一样长度的绳子呢?将最大的长度输出,保留两位小数。

思路:这题我当时做的时候还有25分钟,想了5分钟完全想不出来啊。。。这算什么题目。。。用到的知识点都没看出来。。。

我原来有N条绳子,现在要M条,如果M小于这个N的话,那我按照所有的绳子最小的那根长度,直接给你或者把长的切成这么短给你,也能满足题目要求呀。

我原来有N条绳子,现在要M条,如果M大于这个N的话,那我把绳子不切全给你,都不够M条,肯定要切。可是要切几段呢?

一根绳子平均分切成两条肯定比平均切成三条要长欸,但是哪些绳子要切哪些绳子不切,我怎么晓得啊?

题目说M条一毛一样长的绳子,这个绳子谁知道要多长啊?

先不管符合不符合题目要求的“最长”,如果这个绳子短一点,原来的每根绳子都贡献一条,那自然是极好的。那长点的绳子多贡献一点,短一点的绳子少贡献一点莫。那这个绳子到底多长呢?

我一直有一个遗憾,在集训队的时候,不管遇见还是没有遇见相关算法题,我总是不能get到所谓的“二分答案“的灵感所在,也一直get不到什么时候用这个技巧。

心随意动,就随口一扯,这该不会是二分这个长度吧。。。

然后细想:还真是欸!

因为我不知道题目所说的M条一毛一样长的绳子,再最长的情况下到底是多长,那就直接二分这个长度呗。然后看原来的绳子,能不能凑出这个长度,能凑出几条,凑出来的条数加起来,看看是不是M条,如果长为ans的绳子,大伙儿(就是那些绳子)齐心协力,还凑不到M条,说明ans长度的绳子太长了,需要缩短一点;如果超过M条了,那就说明ans长的绳子太短了,需要延长一点,也有可能还是M条(因为M是整数),那说明还能更长啊!继续延长呗!(这就是二分过程中,”等于号“时的判断方式)

最后直到这个ans的精度达到标准了,就OK输出即可!

 

其中还有两个要注意的地方:假定知道ans以后,怎么求每根没有剪过的原始绳子,能做出几条ans长度的绳子呢?除法!除法啊兄弟!除法不就是做这个的吗? 7/3=2 不就代表7里能凑出2个3的意思吗!

以及,我刚开始用1e-8作为精度考察临界的时候,超时了!!!拍脑袋写代码一气呵成写了10分钟,居然最后超时了,一个样例也没有过(ˉ▽ˉ;)...!N是1e5的数据量,我感觉我已经优化不了了。。。突然一看,题目说只要精确到小数点两位!!!此时还有1分钟就交卷了,死马当做活马医,我把精度改成0.1交一发,把精度改成0.05交了一发,又把精度改成0.005与0.0005交了一发。结果,0.1的精度是过了40%的样例,后面直接就过了!

难度:一星思维题

代码如下

#include<bits/stdc++.h>
#include<iostream>
using namespace std;
#define inf 100009
#define eps 1e-8
#define INF 0x3f3f3f3f
#define loop(x,y,z) for(x=y;x<z;x++)

int n,m;
double e[inf];

int main()
{
    cin>>n>>m;
    int i,j;
    double l=0,r=0,ans;
    loop(i,0,n)//输入,选取最大的绳子作为右边界
    {
        cin>>e[i];
        r=r<e[i]?e[i]:r;//max
    }
    while(r-l>0.0005)//精度
    {
        ans=(l+r)/2;

        int sum=0;
        for(i=0;i<n;i++)	//凑数量
            sum+=e[i]/ans;
        if(sum>=m)l=ans;	//如果凑出了M条,说明还能这个ans还能变更长啊
        else r=ans;		//不够M条,说明当前的长度太长了
    }
    printf("%.2f\n",l);
    return 0;
}

 

 


笔试感悟:

1. 字节跳动的这一次笔试应该只是试水吧,毕竟像我这样的废物都能AK,说明这一次的笔试可能只是为了去除非科班的或者是机器人?毕竟跳水了。

2. 即使是这样的笔试题,我用完了2个小时的时间才做完,我感觉我更是废物啊

3. 虽然题目的营养不高,能力没有,全是水题,但是这中超出我预期,明知道是水分十足,但在除了课程以外的地方AK的感觉,也是真舒服愉悦鸭

4. 加油

评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值