【编程笔试】美团2021校招笔试-通用编程题第4场(附思路及C++代码)

导览

      • 练习地址
      • 小团无路可逃
      • 小团的神秘暗号
      • 小团的选调计划
      • 小团的默契游戏
      • 总结

练习地址

点此前往练习

小团无路可逃

小团惹小美生气了,小美要去找小团“讲道理”。小团望风而逃,他们住的地方可以抽象成一棵有n个结点的树,小美位于x位置,小团位于y位置。小团和小美每个单位时间内都可以选择不动或者向相邻的位置转移,很显然最终小团会无路可逃,只能延缓一下被“讲道理”的时间,请问最多经过多少个单位时间后,小团会被追上。

输入描述:

输入第一行包含三个整数n,x,y,分别表示树上的结点数量,小美所在的位置和小团所在的位置。接下来有n-1行,每行两个整数u,v,表示u号位置和v号位置之间有一条边,即u号位置和v号位置彼此相邻。(1<=n<=50000)

输出描述:

输出仅包含一个整数,表示小美追上小团所需的时间。

输入例子1:

5 1 2

2 1

3 1

4 2

5 3

输出例子1:

2

思路:

使用深搜,分别以 x,y为起点搜索到所有其他点的路径长度,然后取dx > dy得到最大的dx。

Ref:参考文章

代码:

#include
using namespace std;

void dfs(vector<vector<int>>& tree, vector<int>& dis, int u, int fa, int step) 
{
    for(int v :tree[u]) 
	{
        if(v==fa) continue;
        dis[v]=step+1;
        dfs(tree,dis,v,u,step+1);
    }
}

int main()
{
    int n,x,y;
    cin>>n>>x>>y;
    vector<vector<int>> tree(n+1);
    int out = 0;
    for(int i = 1; i <= n-1; i++) 
	{
        int u,v;
        cin>>u>>v;
        tree[u].push_back(v);
        tree[v].push_back(u);
    }
    vector<int> disx(n+1, 0);
    vector<int> disy(n+1, 0);
    dfs(tree, disx, x, -1, 0);
    dfs(tree, disy, y, -1, 0);
    for(int i = 1; i <= n; i++) 
	{
        if(disx[i]>disy[i]) 
			out = max(out, disx[i]);
    }
    cout<<out;
    return 0;
}

小团的神秘暗号

小团深谙保密工作的重要性,因此在某些明文的传输中会使用一种加密策略,小团如果需要传输一个字符串S,则他会为这个字符串添加一个头部字符串和一个尾部字符串。头部字符串满足至少包含一个“MT”子序列,且以T结尾。尾部字符串需要满足至少包含一个“MT”子序列,且以M开头。例如AAAMT和MAAAT都是一个合法的头部字符串,而MTAAA就不是合法的头部字符串。很显然这样的头尾字符串并不一定是唯一的,因此我们还有一个约束,就是S是满足头尾字符串合法的情况下的最长的字符串。
很显然这样的加密策略是支持解码的,给出你一个加密后的字符串,请你找出中间被加密的字符串S。

输入描述:

输入第一行是一个正整数n,表示加密后的字符串总长度。(1<=n<=100000)输入第二行是一个长度为n的仅由大写字母组成的字符串T。

输出描述:

输出仅包含一个字符串S。

输入例子1:

10

MMATSATMMT

输出例子1:

SATM

思路:

使用贪心的想法,找最短的前后缀,即正向找到一次MT,反向找到一次TM。

Ref:参考文章

代码:

#include
using namespace std;

int main()
{
	int n;
	cin>>n;
	string s;
	cin>>s;
	bool m=false,t=false;
	int i,j;
	for(i=0;i<s.length();i++)
	{
		if(s[i]=='M'&&!m)
			m=true;
		else if(s[i]=='T'&&m)
		{
			i++;
			break;
		}
	}
	for(j=s.length()-1;j>=0;j--)
	{
		if(s[j]=='T'&&!t)
			t=true;
		else if(s[j]=='M'&&t)
			break;
	}
	cout<<s.substr(i,j-i);
	return 0;
}

小团的选调计划

美团打算选调n名业务骨干到n个不同的业务区域,本着能者优先的原则,公司将这n个人按照业务能力从高到底编号为1 ~ n。编号靠前的人具有优先选择的权力,每一个人都会填写一个意向,这个意向是一个1~n的排列,表示一个人希望的去的业务区域顺序,如果有两个人同时希望去某一个业务区域则优先满足编号小的人,每个人最终只能去一个业务区域。

例如3个人的意向顺序都是1 2 3,则第一个人去1号区域,第二个人由于1号区域被选择了,所以只能选择2号区域,同理第三个人只能选择3号区域。

最终请你输出每个人最终去的区域。

输入描述:

输入第一行是一个正整数n,表示业务骨干和业务区域数量。(n≤300)接下来有n行,每行n个整数,即一个1~n的排列,第i行表示i-1号业务骨干的意向顺序。

输出描述:

输出包含n个正整数,第i个正整数表示第i号业务骨干最终去的业务区域编号。

输入例子1:

5

1 5 3 4 2

2 3 5 4 1

5 4 1 2 3

1 2 5 4 3

1 4 5 2 3

输出例子1:

1 2 5 4 3

思路:

使用一个bool数组来记录心仪的业务区域是否被选择了,如果被选择,则跳过;否则选择。

代码:

#include
#define N 310 
using namespace std;

int main()
{
	int n;
	cin>>n;
	int num[N][N];
	bool s[N];
	for(int i=0;i<n;i++)
	{
		for(int j=0;j<n;j++)
		{
			cin>>num[i][j];
		}
	}
	for(int i=0;i<n;i++)
	{
		int j=0;
		while(s[num[i][j]])
			j++;
		s[num[i][j]]=1;
		cout<<num[i][j]<<" ";
	}
	return 0;
}

小团的默契游戏

小团从某不知名论坛上突然得到了一个测试默契度的游戏,想和小美玩一次来检验两人的默契程度。游戏规则十分简单,首先有给出一个长度为n的序列,最大值不超过m。

小团和小美各自选择一个[1,m]之间的整数,设小美选择的是l,小团选择的是r,我们认为两个人是默契的需要满足以下条件:

  1. l小于等于r。

  2. 对于序列中的元素x,如果0

小团为了表现出与小美最大的默契,因此事先做了功课,他想知道能够使得两人默契的二元组一共有多少种。

我们称一个序列A为单调不下降的,当且仅当对于任意的i>j,满足Ai>=Aj

输入描述:

输入第一行包含两个正整数m和n,表示序列元素的最大值和序列的长度。(1<=n,m<=100000)输入第二行包含n个正整数,表示该序列。

输出描述:

输出仅包含一个整数,表示能使得两人默契的二元组数量。

输入例子1:

5 5

4 1 4 1 2

输出例子1:

10

思路:

Ref:参考评论区

代码:

#include 
using namespace std;

int solve(vector<int> &nums, int m) {
    int cnt = 0, n = nums.size();
    for (int r = m; r > 0; --r) {
        // 枚举 l 的最大可能性
        int lo = 0, hi = r, mid;  // [0, r],0是为了方便判断 (r,m+1) 所选序列是非法的
        while (lo < hi) {
            mid = (hi - lo + 1) / 2 + lo;
            // 在 nums 中选出 (0, l) ∪ (r, m+1) 的元素并检测是否单调非降
            int pre = -1, flag = 1;  // nums[i] > 0
            for (int x: nums) {
                if (x < mid || x > r) {
                    if (x < pre) {
                        flag = 0;
                        break;
                    }
                    pre = x;
                }
            }
            if (flag) {  // 尝试把 l 放大的同时记录答案
                lo = mid;
            } else {  // 一定不是,那么把 l 减小
                hi = mid - 1;
            }
        }
        // 如果 lo = 0,证明不管怎么降低 l 都无法构成单调非降,因为 (r,m+1) 所选的子序列有问题
        if (0 == lo) break;
        cnt += lo;
    }
    return cnt;
}

int main() {
    int m, n; cin >> m >> n;
    vector<int> nums(n);
    for (int i = 0; i < n; ++i) {
        scanf("%d", &nums[i]);
    }
    cout << solve(nums, m) << endl;
    return 0;
}

总结

第一题:

  1. 深度优先搜索(DFS)和广度优先搜索(BFS)

    1. 区别:

      1. DFS:类似于树的先序遍历。从图中的一个顶点出发,每次遍历当前访问顶点的邻接点,一直到访问的顶点没有未被访问过的邻接点为止。然后采用依次回退的方式,查看来的路上每一个顶点是否有其它未被访问的邻接点。访问完成后,判断图中的顶点是否已经全部遍历完成,如果没有,以未访问的顶点为起始点,重复上述过程。
      2. BFS:类似于树的层次遍历。从图中的某一顶点出发,遍历每一个顶点时,依次遍历其所有的邻接点,然后再从这些邻接点出发,同样依次访问它们的邻接点。按照此过程,直到图中所有被访问过的顶点的邻接点都被访问到。
    2. 优缺点:

      深搜(DFS) 广搜(BFS)
      优点 (1)能找出所有解决方案;(2)内存需求相对较少。 (1)有效解决最短路径问题,寻找深度小;(2)每个结点只访问一遍。
      缺点 (1)多次遍历;(2)深度大时效率低。 内存消耗较大。

第二题:

  1. C++ string类:
    1. 头文件#include
    2. 创建string对象:string s;
    3. 插入字符串:s.insert(pos,str);pos是要插入的下标,str表示要插入的字符串。
    4. 删除字符串:s.erase(pos,len);pos是要删除的子字符串的起始下标,len表示要删除子字符串的长度。
    5. 提取子串:s.substr(pos,len);pos 是要提取的子字符串的起始下标,len 为要提取的子字符串的长度。
    6. 查找字符串:
      1. s.find(str,pos);str表示要查找的字符串,pos为开始查找的下标,如果不指明,则从第0个字符开始查找。
      2. s.rfind(str,pos);str表示要查找的字符串,pos为最多查找到的下标,如果到了pos所指定的下标还没有找到字符串,则返回一个无穷大值4294967295。
      3. s.find_first_of(str);用于查找子字符串str和字符串s共同具有的字符在字符串中首次出现的位置。

第三题:

暂无。

你可能感兴趣的:(美团笔试,笔记,笔试,编程题,美团)