美团2024年春招第一场笔试【技术】

目录

一,小美的平衡矩阵

题目

解题思路

代码

二,小美的数组询问

题目

解题思路 

代码 

三,小美的 MT

题目

解题思路 

代码

四,小美的朋友关系

题目

解题思路

代码

五, 小美的区间删除

题目

解题思路

代码


一,小美的平衡矩阵

题目

小美拿到了一个 n∗n 的矩阵,其中每个元素是 0 或者 1。

小美认为一个矩形区域是完美的,当且仅当该区域内 0 的数量恰好等于 1 的数量。现在,小美希望你回答有多少个 i∗i 的完美矩形区域。你需要回答 1≤i≤n 的所有答案。

时间限制:C/C++ 1秒,其他语言2秒

空间限制:C/C++ 256M,其他语言512M

输入描述:

第一行输入一个正整数n,代表矩阵大小。接下来的n行,每行输入一个长度为n的01串,用来表示矩阵。1<n<200。

输出描述:

输出n行,第i行输出i*i的完美矩形区域的数量。 

示例1:

输入例子: 4  

                   1010

                   0101

                   1100

                   0011

输出例子: 0

                   7

                   0

                   1

解题思路

我们需要统计完美矩阵的区域,我们需要统计矩阵区域中 0 与 1 的数量,我们可以使用前缀和数组记录 0 与 1 的数量,因为矩阵中只有 0 与 1 所以我们只需要记录 0 或 1 一个值的数量。

我们定义一个前缀和数组 s[n+1][n+1] ,s[ i ] [ j ] 表示矩阵中以 ( 0 , 0 ) 为起点的 i * j 矩阵中 1的数量。我们使用动态规划的思想计算前缀和,状态转移方程如下:

 s[i][j] = g[i][j] + s[i - 1][j] + s[i][j - 1] - s[i - 1][j - 1];

在计算完前缀和数组后,我们枚举边长为偶数的矩阵,计算是否为完美矩阵,边长为奇数的矩阵一定不是完美矩阵。枚举 i*i 矩阵的时候,我们从位置 ( i , i ) 开始,计算以 ( j , k ) 为右下顶点的矩阵中 1 的数量,计算逻辑如下:

int sum = s[j][k] - s[j][k - i] - s[j - i][k] + s[j - i][k - i];

然后判断 1 的数量,是否为  ( i *  i )  / 2 。

代码

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

int main() 
{
    int n;
    cin >> n;
    int g[n+1][n+1];
    int s[n+1][n+1];
    for (int i = 1; i <= n; i++) 
    {
        string str;
        cin >> str;
        int k = 0;
        for (int j = 1; j <= n; j++) 
        {
            g[i][j] = str[k++] - '0';
            //1的数量
            s[i][j] = g[i][j] + s[i - 1][j] + s[i][j - 1] - s[i - 1][j - 1];
        }
    }
 
    for (int i = 1; i <= n; i++) 
    {
        if(i % 2) 
        {
            cout << "0" << '\n';
            continue;
        }
        int res = 0;
        for (int j = i; j <= n; j++) 
        {
            for (int k = i; k <= n; k++) 
            {
                int sum = s[j][k] - s[j][k - i] - s[j - i][k] + s[j - i][k - i];
                if (sum == (i * i) / 2)res++;
            }
        }
        cout << res << '\n';
    }
}

二,小美的数组询问

题目

小美拿到了一个由正整数组成的数组,但其中有一些元素是未知的(用 0 来表示)。

现在小美想知道,如果那些未知的元素在区间[𝑙,𝑟][l,r]范围内随机取值的话,数组所有元素之和的最小值和最大值分别是多少?

共有q次询问。

时间限制:C/C++ 1秒,其他语言2秒

空间限制:C/C++ 256M,其他语言512M

输入描述:

第一行输入两个正整数n,q,代表数组大小和询问次数。

第二行输入n个整数a_i,其中如果输入的a_i为 0,那么说明a_i是未知的。

接下来的q行,每行输入两个正整数 l,r,代表一次询问。

1<n,q<10^5 

0<a_i<10^9

1<=l<=r<=10^9

输出描述:

输出q行,每行输出两个正整数,代表所有元素之和的最小值和最大值。

示例1

输入例子: 3 2

                   1 0 3

                   1 2

                   4 4

输出例子: 5 6

                   8 8

例子说明:

只有第二个元素是未知的。

第一次询问,数组最小的和是 1+1+3=5,最大的和是 1+2+3=6。

第二次询问,显然数组的元素和必然为 8。 

解题思路 

我们只需统计所有已知数的和,与未知数的数量,即可根据范围算出最大值与最小值。

代码 

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

long long know_sum=0;
long long unknow_num=0;

void func(int left,int right)
{
    long long min=know_sum+unknow_num*left;
    long long max=know_sum+unknow_num*right;
    cout << min <<" "<< max << endl;
}

int main() 
{
    long long n,q;
    cin >> n;
    cin >> q;
    vector<int> arr(n);
    for(int i=0;i<n;i++)
    {
        cin >> arr[i];
        if(arr[i])
        {
            know_sum+=arr[i];
        }
        else {
            unknow_num++;
        }
    }
    
    for(int i=0;i<q;i++)
    {
        long long left,right;
        cin >> left;
        cin >> right;
        func(left,right);
    }
    return 0;
}

三,小美的 MT

题目

MT 是美团的缩写,因此小美很喜欢这两个字母。

现在小美拿到了一个仅由大写字母组成字符串,她可以最多操作 𝑘 k次,每次可以修改任意一个字符。

小美想知道,操作结束后最多共有多少个'M'和'T'字符?

时间限制:C/C++ 1秒,其他语言2秒

空间限制:C/C++ 256M,其他语言512M

输入描述:

第一行输入两个正整数n,k,代表字符串长度和操作次数。

第二行输入一个长度为n的、仅由大写字母组成的字符串。

输出描述: 

输出操作结束后最多共有多少个'M'和'T'字符。

示例1

输入例子: 5 2

                   MTUAN

输出例子: 4

例子说明: 修改第三个和第五个字符,形成的字符串为 MTTAM,这样共有 4 个'M'和'T'。 

解题思路 

我们只需统计 M 与 T 字符的数量,统计后加上修改次数,减去溢出大小即可。

代码

#include <iostream>
using namespace std;

int main() 
{
    string s;
    int n,k;
    cin >> n >> k;
    cin >> s;
    int ret=0;
    for(int i=0;i<n;i++)
    {
        if(s[i]=='M' || s[i]=='T')
        {
            ret++;
        }
    }
    ret+=k;
    if(ret>n)
    {
        ret=n;
    }
    cout << ret << endl;
    return 0;
}

四,小美的朋友关系

题目

小美认为,在人际交往中,但是随着时间的流逝,朋友的关系也是会慢慢变淡的,最终朋友关系就淡忘了。

现在初始有一些朋友关系,存在一些事件会导致两个人淡忘了他们的朋友关系。小美想知道某一时刻中,某两人是否可以通过朋友介绍互相认识?

事件共有 2 种:
1 u v:代表编号 u 的人和编号 v 的人淡忘了他们的朋友关系。
2 u v:代表小美查询编号 u 的人和编号 v 的人是否能通过朋友介绍互相认识。

注:介绍可以有多层,比如 2 号把 1 号介绍给 3 号,然后 3 号再把 1 号介绍给 4 号,这样 1 号和 4 号就认识了。

时间限制:C/C++ 1秒,其他语言2秒

空间限制:C/C++ 256M,其他语言512M

输入描述:

第一行输入三个正整数n,m,q,代表总人数,初始的朋友关系数量,发生的事件数量。

接下来的m行,每行输入两个正整数u,v,代表初始编号u的人和编号v的人是朋友关系。

接下来的q行,每行输入三个正整数op,u,v,含义如题目描述所述。 保证至少存在一次查询操作。

输出描述:

对于每次 2 号操作,输出一行字符串代表查询的答案。

如果编号 u 的人和编号 v 的人能通过朋友介绍互相认识,则输出"Yes"。否则输出"No"。

示例1

输入例子: 5 3 5

                   1 2

                    2 3

                    4 5

                    1 1 5

                    2 1 3

                    2 1 4

                    1 1 2

                    2 1 3

输出例子: Yes

                   No

                   No

例子说明:

第一次事件,1 号和 5 号本来就不是朋友,所以无事发生。

第二次事件是询问,1 号和 3 号可以通过 2 号的介绍认识。

第三次事件是询问,显然 1 号和 4 号无法互相认识。

第四次事件,1 号和 2 号淡忘了。

第五次事件,此时 1 号无法再经过 2 号和 3 号互相认识了。

解题思路

我们可以使用并查集,存储朋友的关系,在查询时,我们确定关系时,通过寻找根结点,如果找到的根节点相同时,则代表他们可以成为朋友。

但是我们如何处理删除操作呢?我们采用逆向操作,我们接受并存储朋友间的关系,同时初始化并查集,即将他们对应的父节点设为他们自己。

然后我们接受操作输入并存储,如果操作数为 2 直接储存。如果操作数为 1 ,我们判断关系是否存在,不存在我们忽略操作,存在我们删除存储的关系同时存储。

接受操作后我们根据现有的的关系建立并查集,然后逆向遍历操作数组。如果操作数为 1 ,我们将关系加入并查集,如果操作数为 2 , 我们查询二者的关系并存储结果。遍历完成后,逆向输出结果。

代码

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

//并查集
map<int,int> fa;

typedef struct
{
    int op;
    int u;
    int v;
}node;

int find(int x)
{
    while(fa[x] != x) x = fa[x] = fa[fa[x]];
    return x;
}
void merge(int u,int v)
{
    int u_fa=find(u);
    int v_fa=find(v);
    if (u_fa != v_fa) 
        fa[u_fa] = v_fa; 
}

int main() 
{
    int n,m,q;
    cin >> n >> m >> q;
    set<pair<int,int>>  st;    //关系集合 

    for(int i=0;i<m;i++)
    {
        int u,v;
        cin >> u >> v;
        st.insert({u,v});     //存储关系
        fa[u]=u;
        fa[v]=v;                                                                                    
    }
    node OP[q];                
    int num=0;
    for(int i=0; i<q; i++)
    {
        int op, u, v; cin >> op >> u >> v;
        //查询操作,可以直接存入
        if(op == 1){
            if(st.find({u, v}) != st.end()) st.erase({u, v});
            else if(st.find({v, u}) != st.end()) st.erase({v, u});
            else continue; 
        }
        // 存入新的操作数组中
        OP[num++] = {op, u, v};
    }
    //建立并查集
    for(auto& e: st)
    {
        int u=e.first;
        int v=e.second;
        merge(u,v);
    }
    vector<bool> ret; 
    for(int i=num-1;i>=0;i--)
    {
        int op=OP[i].op,u=OP[i].u,v=OP[i].v;
        if(op == 1)
        {
            merge(u,v);
        }
        else
        {
            if(!fa[u]) fa[u] = u;
            if(!fa[v]) fa[v] = v;
            if(find(u) == find(v))
                ret.push_back(true);
            else 
                ret.push_back(false);            
        }
    }
    for(int i=ret.size()-1;i>=0;i--)
    {
        if(ret[i])
            cout << "Yes" <<endl;
        else
            cout << "No" <<endl;
    }
    return 0;
}

五, 小美的区间删除

题目

小美拿到了一个大小为n的数组,她希望删除一个区间后,使得剩余所有元素的乘积末尾至少有 k个 0。

小美想知道,一共有多少种不同的删除方案?

时间限制:C/C++ 1秒,其他语言2秒

空间限制:C/C++ 256M,其他语言512M

输入描述:

第一行输入两个正整数n,k。

第二行输入n个正整数a_i,代表小美拿到的数组。

输出描述:

一个整数,代表删除的方案数。

示例1

输入例子: 5 2

                   2 5 3 4 20

输出例子: 4

例子说明:

第一个方案,删除[3]。

第二个方案,删除[4]。

第三个方案,删除[3,4]。

第四个方案,删除[2]。

解题思路

乘积末尾的 0 数量取决于什么?末尾0的数量由因子2和5中较小的数量决定。所以我们可以通过两个前缀和数组,分别存储 5 与 2 的因子数量。

然后我们可以通过计算区间 [ i , j ] 外的5 与 2 的因子数量的较小值,计算出区间是否可删除。

我们使用双指针探测,找到可删除区间。

代码

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

int n,k;

int kzero(vector<int>& prev_two,vector<int>& prev_five,int left,int right)
{
    return min(prev_five[n] - prev_five[right] + prev_five[left-1],prev_two[n] - prev_two[right] + prev_two[left-1]);
}


int main() 
{
    cin >> n >> k;
    vector<int> x(n+1,0);
    vector<int> prev_two(n+1,0);
    vector<int> prev_five(n+1,0);
    for(int i=1;i<=n;i++)   cin >> x[i];

    //统计5与2的前缀和
    for(int i=1;i<=n;i++)
    {
        int  five=0,two=0;
        while(x[i]%5==0)
        {
            x[i]/=5;
            five++;
        }
        while(x[i]%2==0)
        {
            x[i]/=2;
            two++;
        }
        prev_two[i]=prev_two[i-1]+two;
        prev_five[i]=prev_five[i-1]+five;
    }
    int l=0,r=0;
    long long ans=0;
    //搜素
    for(int i=1, j=1; i<=n && j<=n;)
    {
        //找到或 i=j+1
        while(j<=n && kzero(prev_two,prev_five,i,j)>=k) j++;
        //[i,j-1]有效  || i==j则 [i,i]不符合
        ans+=1LL*(j-i)*(j-i+1)/2;
        //重合  删除 [i,r-1]
        if(r>=i)
            ans-=1LL*(r-i)*(r-i+1)/2;
        l=i,r=j;    //更新区间
        //搜索
        while(i<=j && kzero(prev_two,prev_five,i,j)<k)
            i++;
        j=max(i,j);
    }
    cout << ans ;
    return 0;
}

 

 

  • 15
    点赞
  • 9
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
20233月11日,美团春季招聘笔试中共包含五道编程题目。以下是对每道题目的简要说明: 1. 题目一:这道题目要求解决一个数字统计的问题。可能涉及到的知识点包括数据结构、循环和条件判断等。解决问题的思路可能是使用字典等数据结构来保存统计结果,并使用循环逐个读取输入数据并进行统计。 2. 题目二:这道题目可能是一个字符串处理的问题。需要使用字符串的方法进行操作,如提取、拼接、查找和替换等。可能的解决思路包括使用正则表达式、切片和遍历等。 3. 题目三:这道题目可能涉及到算法数据结构的知识。可能是一道涉及到数组、链表、树等数据结构的问题。解决思路可能包括遍历、递归、搜索和排序等。 4. 题目四:这道题目可能是一个动态规划的问题。需要根据给定的条件和规则,通过动态规划的方式求解问题。解决思路包括定义状态和转移方程,使用递推或记忆化搜索进行求解。 5. 题目五:这道题目可能是一个图论或网络问题。需要根据给定的图或网络结构,解决一个相关的问题。可能涉及到广度优先搜索、深度优先搜索、最短路径等知识。解决思路可能包括使用图或网络的相关算法进行求解。 以上只是对这五道编程题目的一些可能情况进行的简要描述,具体的题目内容可能会有所不同。希望这些信息能对你有所帮助!
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

knight-n

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值