2024年美团春季第一场笔试题

美团2024春招技术岗-第一次笔试

总览

难度适中,不算很难。

前三道题目属于签到类型的题目,大多数玩家都能 AC,比较烦人的是第四题和第五题。

第一题-小美的MT

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

现在小美拿到了一个仅由大写字母组成字符串,她可以最多操作k次,每次可以修改任意一个字符。小美想知道,操作结束后最多共有多少个’M’和’T’字符?

输入描述

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

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

1 < = k < = n < = 1 0 5 {1<=k<=n<=10^5} 1<=k<=n<=105

输出描述

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

输入

5 2
MTUAN

输出

4

说明

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

思路与代码

这道题是基本的字符串处理,虽然很简单,但是我们一定要注意一些细节问题。

我们可以先找出原字符串中有多少个 'M''T',然后再加上我们可以修改 k次,最多产生kM或者 T

最后,千万不要忘了异常处理(取最小值)。

#include <iostream>
#include <cstring>
#include <algorithm>

using namespace std;

const int N = 100010;
int n, m, cnt;
char str[N];
int main() {
	cin >> n >> m;
	cin >> str;
	for (int i = 0; i < n; i ++) {
		if (str[i] == 'M' || str[i] == 'T') {
			cnt ++;
		}
	}
	cout << (cnt + min(m, n - cnt)) << endl;
	return 0;
}

第二题-小美的数组询问

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

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

共有q次询问。

输入描述

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

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

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

  • 1 < = n , q < = 1 0 5 {1<=n,q<=10^5} 1<=n,q<=105
  • 0 < = a i < = 1 0 9 {0<=a_i<=10^9} 0<=ai<=109
  • 1 < = l < = r < = 1 0 9 {1<=l<=r<=10^9} 1<=l<=r<=109

输出描述

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

输入

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 <cstring>
#include <algorithm>

using namespace std;

const int N = 100010;
int s[N];
int n, q;
int l, r;
int num, sum, zero_sum;
int max_v, min_v;

int main() {
	cin >> n >> q;
	for (int i = 0; i < n; i ++) {
	    cin >> s[i];
	    if (!s[i]) {
	        zero_sum ++;
	    }
	    sum += s[i];
	}
	while(q --) {
		cin >> l >> r;
		max_v = sum + zero_sum * r;
		min_v = sum + zero_sum * l;
		cout << min_v << " " << max_v << endl;
	}
	return 0;
}

第三题-小美的平衡矩阵

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

小美认为一个矩形区域是完美的,当且仅当该区域内 0 的数量恰好等于 1 的数量。

现在,小美希望你回答有多少个i*i的完美矩形区域。

你需要回答 1 < = i < = n {1<=i<=n} 1<=i<=n的所有答案。

输入描述

第一行输入一个正整数n,代表矩阵大小。

接下来的n行,每行输入一个长度为 n01串,用来表示矩阵。

输出描述

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

输入

4
1010
0101
1100
0011

输出

0
7
0
1

思路与代码

这道题,我们先需要对输入字符串进行处理,然后再使用二维前缀和去统计子区域中1的个数,然后在逐步遍历k大小的子区域进行判断即可。

#include <iostream>
#include <cstring>
#include <algorithm>

using namespace std;

const int N = 1000;
int n;
char str[N][N];
int m[N][N], s[N][N];

int ans[N];

int GetPreSub(int x1, int y1, int x2, int y2) {
    return s[x2 + 1][y2 + 1] - s[x1][y2 + 1] - s[x2 + 1][y1] + s[x1][y1];
}

int main() {
    cin >> n;
    for (int i = 0; i < n; i++) cin >> str[i];
    
    for (int i = 0; i < n; i ++) {
        for (int j = 0; j < n; j ++) {
            if (str[i][j] == '1') {
                m[i + 1][j + 1] = 1;
            } else {
                m[i + 1][j + 1] = 0;
            }
        }
    }
    
    for (int i = 1; i <= n; i ++) {
        for (int j = 1; j <= n; j  ++) {
            s[i][j] = s[i - 1][j] + s[i][j - 1] - s[i - 1][j - 1] + m[i][j];
        }
    }
    for (int i = 0; i < n; i ++) { 
        for (int j = 0; j < n; j ++) {
            for (int k = 0; k < n; k ++) {
                if(i + k >= n || j + k >= n) {
                    break;
                }
                int sub = GetPreSub(i, j, i + k, j + k);
                if (sub * 2 == (k + 1) * (k + 1)) {
                    ans[k] ++;
                }
            }
        }
    }


    for (int i = 0; i < n; i ++) cout << ans[i] << endl;
    return 0;
}

第四题-小美的区间删除

小美拿到了一个大小为n的数组,她希望删除一个区间后,使得剩余所有元素的乘积末尾至少有k0。小美想知道,一共有多少种不同的删除方案?

输入描述

第一行输入两个正整数nk

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

1 < = n , k < = 1 0 5 {1<=n,k<=10^5} 1<=n,k<=105

1 < = a i < = 1 0 9 {1<=a_i<=10^9} 1<=ai<=109

输出描述

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

输入

5 2
2 5 3 4 20

输出

4

说明

第一个方案,删除[3]。
第二个方案,删除[4]。
第三个方案,删除[3,4]。
第四个方案,删除[2]。

思路与代码

#include <iostream>
#include <vector>
#include <algorithm>
#include <numeric>

using namespace std;

// 获取因子2的数量
int getFactor2(int a) {
    int f2 = 0;
    while (a != 0 && a % 2 == 0) {
        f2++;
        a /= 2;
    }
    return f2;
}

// 获取因子5的数量
int getFactor5(int a) {
    int f5 = 0;
    while (a != 0 && a % 5 == 0) {
        f5++;
        a /= 5;
    }
    return f5;
}

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

    vector<int> f2s(n), f5s(n); // 各元素的因子2和5的数量
    for (int i = 0; i < n; i++) {
        f2s[i] = getFactor2(A[i]);
        f5s[i] = getFactor5(A[i]);
    }

    int all2 = accumulate(f2s.begin(), f2s.end(), 0); // 所有元素的因子2的总数量
    int all5 = accumulate(f5s.begin(), f5s.end(), 0); // 所有元素的因子5的总数量
    
    vector<int> pres2(n + 1), pres5(n + 1); // 前缀和
    partial_sum(f2s.begin(), f2s.end(), pres2.begin() + 1);
    partial_sum(f5s.begin(), f5s.end(), pres5.begin() + 1);

    long long cnt = 0;
    for (int i = 0; i < n; i++) {
        auto p2 = upper_bound(pres2.begin(), pres2.end(), pres2[i + 1] + all2 - k) - pres2.begin();
        auto p5 = upper_bound(pres5.begin(), pres5.end(), pres5[i + 1] + all5 - k) - pres5.begin();
        cnt += min(p2, p5) - i - 1;
    }

    cout << cnt << endl;
    return 0;
}

第四题-小美的朋友关系

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

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

事件共有 2 种:

  • 1 u v:代表编号 u 的人和编号 v 的人淡忘了他们的朋友关系。

  • 2 u v:代表小美查询编号 u 的人和编号 v 的人是否能通过朋友介绍互相认识。

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

输入描述

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

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

接下来的q行,每行输入三个正整数op,u,v,含义如题目描述所述。

1 < = n < = 1 0 9 {1 <= n <= 10^9} 1<=n<=109

1 < = m , q < = 1 0 5 {1 <= m,q <=10^5} 1<=m,q<=105

1 < = u , v < = n {1 <=u,v <= n} 1<=u,v<=n

1 < = o p < = 2 {1 <= op <= 2} 1<=op<=2

保证至少存在一次查询操作。

输入

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 号互相认识了。

思路与代码

这道题我在开始的时候,使用到了并查集来实现,但是在想办法删除的时候无从下手,所以我使用到了反向并查集的操作

对于查询操作,我们不改变,还是查询即可。

但是对于删除操作,我们则往并查集里加边即可。

正所谓,正难则反。

#include <iostream>
#include <vector>
#include <set>
#include <map>

using namespace std;

map<int, int> fa;

int find(int x) {
    if (x == fa[x]) return x;
    fa[x] = find(fa[x]);
    return fa[x];
}

void union_set(int x, int y) {
    fa[find(x)] = find(y);
}

int main() {
    int n, m, q;
    cin >> n >> m >> q;

    // 初始化
    for (int i = 1; i <= n; ++i) fa[i] = i;

    set<pair<int, int>> edges, del_edges;
    for (int i = 0; i < m; ++i) {
        int u, v;
        cin >> u >> v;
        edges.insert({u, v});
    }

    vector<tuple<int, int, int>> ops(q);
    for (int i = 0; i < q; ++i) {
        int op, u, v;
        cin >> op >> u >> v;
        ops[i] = {op, u, v};
        if (op == 1) {
            if (edges.count({u, v})) del_edges.insert({u, v});
            if (edges.count({v, u})) del_edges.insert({v, u});
        }
    }

    // 逆向构图
    for (auto& [u, v] : edges) {
        if (del_edges.count({u, v}) || del_edges.count({v, u})) {
            continue;
        }
        union_set(u, v);
    }

    vector<string> ans;
    // 逆向遍历
    for (int i = q - 1; i >= 0; --i) {
        auto [op, u, v] = ops[i];
        if (op == 2) {
            ans.push_back(find(u) == find(v) ? "Yes" : "No");
        }
        else {
            union_set(u, v);
        }
    }

    for (auto iter = ans.rbegin(); iter != ans.rend(); ++iter) {
        cout << *iter << endl;
    }

    return 0;
}

总结

本次是我考研完之后第一次参加算法笔试,有些算法彻底遗忘了,对自己在比赛时的表现中下吧,下次再战。

  • 15
    点赞
  • 45
    收藏
    觉得还不错? 一键收藏
  • 4
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值