美团算法笔试

挺难的,感觉比上周难☹️,8 选择 + 4 编程,120 分钟。T1 签到,T2 是 sklearn 大题跳过了,T3 折半搜索经典套路,T4 笛卡尔树分治有点东西。

选择题:RAG 文档更新后检索旧内容的原因、SVM 模型压缩方法、迁移学习微调策略、CV 预训练任务选择、GBDT 特性、二分查找比较次数、度为4的树求叶结点数

T1. 风不吹雨

每个位置可以做操作1(除以2取整,最多 次总共)和操作2(减 ,最多 次总共),求最小总和。

两种操作的收益是独立的:操作1的收益是 (不管有没有做操作2),操作2的收益恒为 (不管有没有做操作1)。所以直接贪心:操作1选收益最大的 个位置,操作2随便选 个位置(收益都是 )。

(顺带一提,两种操作同时做的时候,先做操作1再做操作2更优,因为先砍半再减 比先减 再砍半少得更多。不过因为收益可分离,这个细节不影响最终答案。)

参考代码:

#include<bits/stdc++.h>
using namespace std;
#define int long long
signed main(){
    ios::sync_with_stdio(false);cin.tie(0);
    int _;cin>>_;
    while(_--){
        int n,ca,cb,k,i;
        cin>>n>>ca>>cb>>k;
        int s=0;
        vector<int>sv(n);
        for(i=0;i<n;i++){
            int x;cin>>x;
            s+=x;
            sv[i]=(x+1)/2;
        }
        sort(sv.rbegin(),sv.rend());
        int s1=0;
        for(i=0;i<min(ca,n);i++)s1+=sv[i];
        cout<<s-s1-min(cb,n)*k<<"\n";
    }
}

T2. SVM异常检测

参考代码:

import numpy as np
import pandas as pd
from sklearn.model_selection import train_test_split
from sklearn.svm import OneClassSVM
from sklearn.metrics import roc_auc_score, f1_score

def solve(train_data, test_data, gamma_list, nu_list):
    X = train_data.drop(columns=['y'])
    y = train_data['y']

    # Step 1: 数据拆分
    X_normal = X[y == 0]
    X_anomaly = X[y == 1]
    X_tr, X_val_norm = train_test_split(X_normal, test_size=0.25, random_state=42, shuffle=True)
    X_val_anom = X_anomaly

    # Step 2: 标准化(只用 X_tr 估计 mean/std)
    mean = X_tr.mean(axis=0)
    std = X_tr.std(axis=0)
    std[std == 0] = 1
    X_tr_s = (X_tr - mean) / std
    X_val_norm_s = (X_val_norm - mean) / std
    X_val_anom_s = (X_val_anom - mean) / std

    X_val = pd.concat([X_val_norm_s, X_val_anom_s])
    y_val = np.array([0]*len(X_val_norm_s) + [1]*len(X_val_anom_s))

    # Step 3-4: 网格搜索 + 评估
    results = []
    for gamma in gamma_list:
        for nu in nu_list:
            model = OneClassSVM(kernel="rbf", gamma=gamma, nu=nu,
                                shrinking=False, tol=1e-4, cache_size=200, max_iter=-1)
            model.fit(X_tr_s)

            scores = -model.decision_function(X_val)  # 取负!
            auc = roc_auc_score(y_val, scores)

            preds = model.predict(X_val)
            y_pred = (preds == -1).astype(int)  # -1→异常(1), +1→正常(0)
            f1 = f1_score(y_val, y_pred)

            results.append({'gamma': gamma, 'nu': nu, 'auc': auc, 'f1': f1})

    # Step 5: 选最优(AUC降序 → F1降序 → nu升序 → gamma升序)
    results.sort(key=lambda x: (-x['auc'], -x['f1'], x['nu'], x['gamma']))
    best = results[0]

    # Step 6: 全量正常数据重训 + 预测
    X_full = pd.concat([X_tr_s, X_val_norm_s])
    best_model = OneClassSVM(kernel="rbf", gamma=best['gamma'], nu=best['nu'],
                              shrinking=False, tol=1e-4, cache_size=200, max_iter=-1)
    best_model.fit(X_full)

    X_test_s = (test_data - mean) / std
    test_preds = best_model.predict(X_test_s)
    return (test_preds == -1).astype(int)

T3. 糟糕,忘记给红黑树浇水了

天(),每天三种选择:给红树加 ,给黑树加 ,或者跳过(最多跳 天)。最小化红树和黑树高度差的绝对值。

直接暴力 肯定不行,但折半搜索 万就很轻松了。

把天数分成前后两半。对前半部分枚举所有 种选择,记录每种方案的(高度差 ,跳过次数 )。按跳过次数分组后排序,再做前缀合并——这样查询"跳过次数 的所有方案中,高度差最接近目标值"就是一个二分。

后半部分同样枚举所有方案,对每个方案在前半的合并表里二分找最优配对。

参考代码:

#include<bits/stdc++.h>
using namespace std;
#define int long long
int a[22],b[22];
signed main(){
    ios::sync_with_stdio(false);cin.tie(0);
    int n,k,i;
    cin>>n>>k;
    for(i=0;i<n;i++)cin>>a[i];
    for(i=0;i<n;i++)cin>>b[i];
    int h=n/2,h2=n-h;
    int pw=1;
    for(i=0;i<h;i++)pw*=3;
    vector<vector<int>>lf(h+1);
    for(int mask=0;mask<pw;mask++){
        int t=mask,d=0,sk=0;
        for(int j=0;j<h;j++){
            int c=t%3;t/=3;
            if(c==0)d+=a[j];
            else if(c==1)d-=b[j];
            else sk++;
        }
        if(sk<=k)lf[sk].push_back(d);
    }
    vector<vector<int>>cum(h+1);
    for(int s=0;s<=h;s++){
        sort(lf[s].begin(),lf[s].end());
        if(s==0)cum[s]=lf[s];
        else{
            cum[s].resize(cum[s-1].size()+lf[s].size());
            merge(cum[s-1].begin(),cum[s-1].end(),lf[s].begin(),lf[s].end(),cum[s].begin());
        }
    }
    int pw2=1;
    for(i=0;i<h2;i++)pw2*=3;
    int ans=LLONG_MAX;
    for(int mask=0;mask<pw2;mask++){
        int t=mask,d=0,sk=0;
        for(int j=0;j<h2;j++){
            int c=t%3;t/=3;
            if(c==0)d+=a[h+j];
            else if(c==1)d-=b[h+j];
            else sk++;
        }
        int al=k-sk;
        if(al<0)continue;
        int s=min(al,(int)h);
        if(cum[s].empty())continue;
        int tg=-d;
        auto it=lower_bound(cum[s].begin(),cum[s].end(),tg);
        if(it!=cum[s].end())ans=min(ans,abs(*it+d));
        if(it!=cum[s].begin()){--it;ans=min(ans,abs(*it+d));}
    }
    cout<<ans<<"\n";
}

T4. 小美的区间

统计区间 )满足 的数量。

一个关键观察:如果区间最大值就是 (即端点之一),那条件自动成立(,恒成立)。只有最大值在内部的时候才可能不满足。

所以对每个"区间最大值"统计贡献。用笛卡尔树分治:找到区间 的最大值位置 ,那么所有跨过 的区间的最大值就是 。端点包含 的 pair 直接计入答案,跨过 的 pair )需要

用 sparse table 做 区间最大值查询,分治时对较小的一侧遍历、在较大一侧的排序数组中二分。递归过程中顺便做归并排序,总复杂度

有个坑:如果元素有大量相同值,笛卡尔树会退化成链,merge 变 。解决办法是给 sparse table 的比较加一个随机 tiebreaker,让等值元素的树结构随机化,避免退化。一开始没加这个 T 了三发才找到原因。

参考代码:

#include<bits/stdc++.h>
using namespace std;
#define int long long
int ori[100020],a[100020],tmp[100020];
int sp[100020][17],lg[100020];
int n,ans;
void build(){
    for(int i=1;i<=n;i++)sp[i][0]=i;
    for(int j=1;(1<<j)<=n;j++)
        for(int i=1;i+(1<<j)-1<=n;i++){
            int x=sp[i][j-1],y=sp[i+(1<<(j-1))][j-1];
            sp[i][j]=ori[x]>=ori[y]?x:y;
        }
    lg[1]=0;
    for(int i=2;i<=n;i++)lg[i]=lg[i/2]+1;
}
int qm(int l,int r){
    int k=lg[r-l+1];
    int x=sp[l][k],y=sp[r-(1<<k)+1][k];
    return ori[x]>=ori[y]?x:y;
}
void solve(int l,int r){
    if(l>r)return;
    if(l==r){a[l]=ori[l];return;}
    int m=qm(l,r);
    solve(l,m-1);solve(m+1,r);
    ans+=(m-l)+(r-m);
    int ls=m-l,rs=r-m;
    if(ls&&rs){
        if(ls<=rs){
            for(int i=l;i<m;i++){
                int th=ori[m]-a[i];
                ans+=(a+r+1)-upper_bound(a+m+1,a+r+1,th);
            }
        }else{
            for(int j=m+1;j<=r;j++){
                int th=ori[m]-a[j];
                ans+=(a+m)-upper_bound(a+l,a+m,th);
            }
        }
    }
    merge(a+l,a+m,a+m+1,a+r+1,tmp);
    for(int i=0;i<ls+rs;i++)a[l+i]=tmp[i];
    a[r]=ori[m];
}
signed main(){
    ios::sync_with_stdio(false);cin.tie(0);
    cin>>n;
    for(int i=1;i<=n;i++)cin>>ori[i];
    build();ans=0;
    solve(1,n);
    cout<<ans<<"\n";
}
#美团笔试#
全部评论
大厂去了就算很好的经历了
点赞 回复 分享
发布于 03-31 15:27 江苏
T4做法强的,唤醒了我死去的记忆
点赞 回复 分享
发布于 03-28 18:52 香港

相关推荐

好的主人,我直接上原汁原味的面试官原话提取版,带时间戳,严格分行。[00:00]Q1:&nbsp;进行一下自我介绍。Q2:&nbsp;你是大几开始接触的&nbsp;Java&nbsp;的?Q3:&nbsp;你入手的时候有其他语言基础吗?就是在学&nbsp;Java&nbsp;之前。Q4:&nbsp;那你在基础&nbsp;Java&nbsp;的时候,你是以什么角度去切入学习的?比如说你的学习树是怎么建立的?Q5:&nbsp;那你在做项目过程中,你觉得自己遇到的最难的困题是啥?Q6:&nbsp;你视频的处理分片是把它分成什么&nbsp;M3U8&nbsp;文件吗?Q7:&nbsp;那你视频流读取过程中用什么请求?它会不会涉及到跨域跳转这些?Q8:&nbsp;那你那个文件上传的时候,像你说的那个断点,就是比如说网络中断的情况下,你是怎么保持后续的工作的?Q9:&nbsp;所以你是记录的一个状态,然后给到前端,那你这里只记录这个状态,只用到&nbsp;Redis&nbsp;是吧?Q10:&nbsp;那你是先写&nbsp;Redis&nbsp;还是先写&nbsp;Mysql?Q11:&nbsp;那如果那个&nbsp;Redis&nbsp;那个就是挂了怎么办?Q12:&nbsp;比如说我举个详细点的例子,比如说&nbsp;Redis&nbsp;某一个节点挂了,那你这个节点挂了之后,就是这个服务就不可用了。还是说?Q13:&nbsp;那你现在用的是主从还是什么模式?Q14:&nbsp;比如说就是你现在是分片上传,那分片合并的时候会不会有重复的情况?Q15:&nbsp;前端去重,你有没有考虑到一个情况?如果你是做一个&nbsp;Web&nbsp;页面,你的前端可能涉及到的鉴权没办法去避免恶意攻击,同样一个请求带着自己的&nbsp;cookie&nbsp;重复的去上传你的服务,会对你服务造成什么影响吗?Q16:&nbsp;令牌桶限流,那你的&nbsp;key&nbsp;是啥呀?Q17:&nbsp;说到限流,你知道的那个限流的算法一共有几个?Q18:&nbsp;令牌桶你说的弹性可能不是很准确,你可以再详细的说一下令牌桶它的所谓的弹性在哪里?Q19:&nbsp;说的没问题。那我再详细地说一下那个令牌,比如说我现在限流是&nbsp;100,5&nbsp;秒极限流&nbsp;100,我上一秒的请求是&nbsp;50,这一秒的请求是&nbsp;150,我会被限流住吗?Q20:&nbsp;ok,讲一下具体的实现机制。Q21:&nbsp;你用那个&nbsp;Rocketmq&nbsp;去解耦的时候,视频上传了之后要经过这三个步骤,是不是需要按照顺序去处理?你怎么用那个&nbsp;Rocketmq&nbsp;去实现这个顺序?Q22:&nbsp;如果你那个没有保那一个视频,假如说你分成了多个段,如果你最终保持顺序一致的话,他会对&nbsp;AI&nbsp;的总结会有影响吗?Q23:&nbsp;所以你就是这个分片只是上传到云端之后,再给他合成一个转码的一个地址,对吧?Q24:&nbsp;我看你也还有一个秒杀的场景,会遇到超卖问题吗?Q25:&nbsp;你做了一些功能防止超卖,你在自己有验证过吗?就是模拟这个场景,超卖场景去验证自己在项目中的这些措施是有效的。Q26:&nbsp;你在进行防超卖的时候会不会导致少卖?就是我库存里有,但是我实际显示我已经卖完了。Q27:&nbsp;我再详细问一下这个&nbsp;Redis&nbsp;加&nbsp;Lua&nbsp;的实现库存扣减,你用的是&nbsp;Redis&nbsp;的&nbsp;decrement&nbsp;加一些原子的操作吗?还是?Q28:&nbsp;只用加减这个命令就能实现吗?还是说要做什么状态判断?Q29:&nbsp;那我再问个问题,比如说我&nbsp;Redis&nbsp;扣减成功了,然后&nbsp;DB&nbsp;写失败了,这个库存怎么处理?Q30:&nbsp;所以我理解就是你要先去&nbsp;DB&nbsp;删除,再去&nbsp;Redis&nbsp;扣减。Q31:&nbsp;那我这里也有个问题,就是那你库存是以&nbsp;DB&nbsp;为准还是以&nbsp;Redis&nbsp;为准?Q32:&nbsp;你&nbsp;DB&nbsp;删了之后,你的订单的判断是还是通过&nbsp;Redis&nbsp;判断,是吧?Q33:&nbsp;比如说你现在以&nbsp;Redis&nbsp;为主,读写在集群真实场景下,写操作在主,读的话是在从。我在清库存的时候主从切换可能导致库存数据不一致。如果是发生这种情况的话,你会怎么考虑去解决这种超卖的风险?Q34:&nbsp;你现在接触的技术栈除了&nbsp;Java&nbsp;之外,还有其他的吗?Q35:&nbsp;你们现在有接触过那个&nbsp;AI&nbsp;相关的东西吗?Q36:&nbsp;那你最近接触的&nbsp;AI&nbsp;的比较火的是啥呀?Q37:&nbsp;就是你在项目实现中有用过&nbsp;AI&nbsp;去生成代码之类的,或者是使用一些&nbsp;AI&nbsp;工具。Q38:&nbsp;比如说让你和&nbsp;AI&nbsp;交互,帮你生成一个推荐网页,它会推荐外卖、酒店、餐厅这种商品信息,你要写一个&nbsp;prompt&nbsp;和模型去交互,你会怎么来说?Q39:&nbsp;ok很好,你之前用的那个&nbsp;AI&nbsp;的那些工具都是用啥?用的&nbsp;CC&nbsp;还是啥?Q40:&nbsp;你那个算法掌握得咋样?Q41:&nbsp;5&nbsp;分的话,你给自己算法打几分?Q42:&nbsp;那挑一道简单的算法给你吧,合并两个有序数组,这个会做嘛?ok,你先做。Q43:&nbsp;学校如果你来实习的话,这边是学校是已经,那个课程已经就是完成了,是吧?Q44:&nbsp;你吉大离这有点远,如果来这边的话是成都的,你家是哪的?Q45:&nbsp;你在那个大学的时候有做过其他的一些,比如说其他的一些项目吗?除了简历上的。Q46:&nbsp;你接触过龙虾吗?就是最近比较火的。Q47:&nbsp;讲一下你用来干什么吧Q48:&nbsp;反问环节。好像没啥可参考的,拷打了40分钟项目,也是浅层的拷打,撕了道简单算法。面试官人很好,反问给了很有用的建议,然后问业务说的也面面俱到我感觉,就是不怎么核心。
点赞 评论 收藏
分享
评论
1
8
分享

创作者周榜

更多
牛客网
牛客网在线编程
牛客网题解
牛客企业服务