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

练习地址

点此前往练习

六位数

小团想要编写一个程序,希望可以统计在M和N之间(M<N,且包含M和N)有多少个六位数ABCDEF满足以下要求:

(1) ABCDEF这六个数字均不相同,即A、B、C、D、E和F表示六个不同的数字。

(2) AB+CD=EF。即将这个六位数拆成三个两位数,使得第1个和第2个两位数的和等于第3个两位数。

(注意:AB、CD和EF都必须是正常的两位数,因此A、C和E都不能等于0。)

输入描述:

单组输入。输入两个六位正整数M和N(M<N),两者之间用空格隔开。

输出描述:

输出在M到N之间(包含M和N)满足要求的六位数的个数。

输入例子1:

100000 110000

输出例子1:

0

思路:

首先对m、n进行判断,当m比n小时,才能进入循环中。对在范围中的六位数不断对10取余,除10,分别得到a-f,最后满足ab+cd=ef条件,计数+1。

代码:

#include<bits/stdc++.h>
using namespace std;

int main()
{
	int m,n;
	cin>>m>>n;
	int count=0;
	if(m<n&&m/100000>=1&&n/100000>=1)
	{
		for(int i=m;i<=n;i++)
		{
			int tmp=i;
			int f=tmp%10;
			tmp/=10;
			int e=tmp%10;
			if(e==f)
				continue;
			tmp/=10;
			int d=tmp%10;
			if(d==e||d==f)
				continue;
			tmp/=10;
			int c=tmp%10;
			if(c==d||c==e||c==f)
				continue;
			tmp/=10;
			int b=tmp%10;
			if(b==c||b==d||b==e||b==f)
				continue;
			tmp/=10;
			int a=tmp%10;
			if(a==b||a==c||a==d||a==e||a==f)
				continue;
			tmp/=10;
			if((a*10+b)+(c*10+d)==(e*10+f))
				count++;
		}
	}
	cout<<count;
	return 0;
}

小美的新游戏

小美和小团合作开发了一款新游戏!他们相信这款游戏一定可以大火。

游戏规则是这样的,现在有一个方格地图,你控制一个机器人位于初始位置(x, y),然后你可以向上下左右的地块移动。其中一些地块上会有得分点,经过这些点可以获得分数。当然,路上还会有一些陷阱点,如果想要通过陷阱点,就需要付出一定的分数来清除这个陷阱点。注意陷阱点付出分数清除后就会变成普通的地块。即反复经过陷阱点只需付出一次代价。同样的,得分点也只能获得一次分数。

小美想到了一个策划案来让这个游戏变得难一些。小美把地图和机器人的初始位置给了小团,并且告诉了小团他操控机器人的行进路线。小美想试试小团能不能算出来他的最终得分。

小团完美地完成了这个任务。现在,小美和小团想找一些测试人员看看这款游戏的难度如何。他们找到了你,希望你帮他们测试一下这个游戏。而你能否挑战成功呢?

注意分数允许为负。初始分数为0.

输入描述:

第一行四个整数N,M,P,Q,表示这张地图是N行M列的,得分点的得分是P,陷阱点清除的代价是Q。接下来N行,每行M个字符,表示这张地图。其中,字符S表示初始机器人位置。字符#表示墙壁,字符O代表得分点。字符X代表陷阱点。字符+代表普通的地块。 接下来一行一个连续的字符串表示机器人的移动路线,只由大写字母WASD构成,W向上,A向左,S向下,D向右。机器人可以上下左右移动。不能超出地图边界。也不能走到墙壁之上。试图走出边界和走到墙壁的行动会停留在原来的位置不动。

输出描述:

一个整数,表示小团的机器人最终获得了多少分

输入例子1:

6 6 20 10

S#++O#

OXX#X#

++++++

###XX#

++#O#+

OXO++X

SSDDDDDAWWSSSAWSSSADDD

输出例子1:

40

思路:

参考评论区刷完这题我就去睡觉的思路:

写一个计分函数,当遇到得分点O,加上p,恢复成普通地块;当遇到陷阱点X,减去q,恢复成普通地块。

使用数据对表示当前位置,当可以前进时,向对应方向前进,计算更新当前分数,再次行进。

代码:

#include<bits/stdc++.h>
using namespace std;

void Score(int i,int j,int &score,vector<vector<char>> &a,int p,int q)
{
	if(a[i][j]=='O')
	{
		score+=p;
		a[i][j]='+';
	}
	if(a[i][j]=='X')
	{
		score-=q;
		a[i][j]='+';
	}
}

int main()
{
	ios::sync_with_stdio(false);
    cin.tie(0);
	int m,n,p,q;
	cin>>n>>m>>p>>q;
	vector<vector<char>> a;
	pair<int,int> pos={0,0};
	int score=0;
	for(int i=0;i<n;i++)
	{
		a.push_back(vector<char>());
		for(int j=0;j<m;j++)
		{
			char c;
			cin>>c;
			if(c=='S')
				pos={i,j};
			a[i].push_back(c);
		}
	}
	string s;
	cin>>s;
	queue<pair<int,int>> qq;
	qq.push(pos);
	for(int i=0;i<s.size();i++)
	{
		auto node=qq.front();
		int row=node.first,col=node.second;
		if(s[i]=='W')
		{
			if(row-1<0||a[row-1][col]=='#')
				continue;
			else
			{
				qq.push({row-1,col});
				Score(row-1,col,score,a,p,q);
				qq.pop();
			}
		}
		if(s[i]=='A')
		{
			if(col-1<0||a[row][col-1]=='#')
				continue;
			else
			{
				qq.push({row,col-1});
				Score(row,col-1,score,a,p,q);
				qq.pop();
			}
		}
		if(s[i]=='S')
		{
			if(row+1>=n||a[row+1][col]=='#')
				continue;
			else
			{
				qq.push({row+1,col});
				Score(row+1,col,score,a,p,q);
				qq.pop();
			}
		}
		if(s[i]=='D')
		{
			if(col+1>=m||a[row][col+1]=='#')
				continue;
			else
			{
				qq.push({row,col+1});
				Score(row,col+1,score,a,p,q);
				qq.pop();
			}
		}
	}
	cout<<score;
	return 0;
}

小美找朋友

小美将自己朋友的名字写在了一块,惊讶地发现她写出的那个字符串S有一个惊人的性质:一个人是小美的朋友当且仅当她/他的名字是那个字符串的子序列。现在小团想根据那个字符串判断一个人是不是小美的朋友。

*子序列:一个字符串A是另外一个字符串B的子序列,当且仅当可以通过在B中删除若干个字符(也可能一个都不删),其他字符保留原来顺序,使得形成的新字符串B’与A串相等。例如,ABC是AABDDC的子序列,而ACB不是AABDDC的子序列。

输入描述:

第一行两个正整数n,m分别表示小美朋友字符串S的长度,以及小团想了解是不是小美朋友的那个人的名字字符串T的长度。

第二行长度为n且仅包含小写字母的字符串S.

第三行长度为m且仅包含小写字母的字符串T.

输出描述:

如果小团想了解的那个人不是小美的朋友(即,T不是S的子序列),输出一行”No”,否则输出一行”Yes”,并在第二行将T对应到S中的位置之和输出出来(从1开始编号),由于可能有多种对应方式,请输出最小的位置之和。请参见样例解释获取更详细说明

输入例子1:

6 3

aabddc

abc

输出例子1:

Yes
10

例子说明1:

对于样例1

S=aabddc T=abc,T中a可以与S中第1个字符a对应起来,b可以与S中第3个字符b对应起来,c可以与S中第6个字符c对应起来,这样一来就找到了一个S中的子序列(仅保留第1、3、6个字符形成的子序列),使其与T相等。这种情况下,位置之和为1+3+6=10

还有一种方案,是S仅保留第2、3、6个字符形成的子序列abc,仍然满足与T相等的条件。但是位置之和为2+3+6=11,并不是位置之和最小的,因而输出第一种对应方案的位置之和:10

输入例子2:

6 3

aabddc

acb

输出例子2:

No

例子说明2:

对于样例2

可以保留S中的第1、3、6个字符,形成子序列abc,但于T串acb不相等,因为b、c位置并不对应。可以证明,没有任何一种取S子序列的方式,可以使得取出的子序列和T相等,因而输出No

思路:

对s和t字符串进行遍历,同时检查当前t中的字符是否与s中的字符相等,相等即把该字符在s中的下标+1后加入总和sum中。直到对s遍历完成,如果在t中所有的字符在s中都顺序出现,即输出Yes和总和sum,否则输出No。

代码:

#include<bits/stdc++.h>
using namespace std;

int main()
{
	int n,m;
	cin>>n>>m;
	string s,t;
	long sum=0;
	int j=0;
	cin>>s;
	cin>>t;
	for(int i=0;i<n;i++)
	{
		if(j<m&&t[j]==s[i])
		{
			j++;
			sum+=i+1;
		}
	}
	if(j==t.size())
	{
		cout<<"Yes"<<endl;
		cout<<sum<<endl;
	}		
	else
		cout<<"No"<<endl;
	return 0;
}

小美的美丽树

小美在观察一棵美丽的无根树。

小团问小美:“小美,我考考你,如果我选一个点为根,你能不能找出子树大小不超过K的前提下,子树内最大值和最小值差最大的子树的根是哪个点?多个点的话你给我编号最小的那个点就行了。”

小美思索一番,说这个问题难不倒他。

输入描述:

第一行两个正整数N和K,表示全树有N个节点,要求子树大小不超过K。第二行是N个正整数空格分隔,表示每个点的点权。以点编号从1到N的顺序给出点权。接下来N-1行每行两个正整数表示哪两个点之间有边相连。最后一行一个正整数root表示小团所选的根节点编号为root。

输出描述:

一行,一个正整数,含义如问题描述,输出在子树大小不超过K的前提下,子树内最大值和最小值差最大的子树的根的编号

输入例子1:

5 2

1 3 2 4 5

1 2

2 3

3 4

4 5

3

输出例子1:

2

思路:

参考评论区零葬的思路:

用邻接表构建一个图,从小团给的根节点开始对图进行深搜。

(1) 以当前节点为根的子树有多少个节点;

(2) 以当前节点为根的子树的最大叶权值和最小叶权值;

(3) 截止到目前为止,子树权值极差的最大值;

(4) 这个最大值对应的最小根节点编号。

得到以上信息后,利用(1)找到节点数不超过k的子树,如果子树的权值极差打破记录,就更新最大权值差和节点;如果等于记录,就看当前子树的根节点编号是否更小;小于记录就直接略过。

以下附上大佬Java代码:

代码:

import java.io.BufferedReader;
import java.io.InputStreamReader;
import java.io.IOException;
import java.util.HashMap;
import java.util.ArrayList;

public class Main {
    static boolean[] visited;   // 标记节点是否已经被访问
    static int[] weight;        // 节点权值
    static int[] childNum;      // 存储以节点i为根的树有多少个节点
    static int[] max, min;      // 存储以节点i为根的子树下的最大值和最小值
    // 节点间的最大差值
    static int maxDiff = -1;
    // 待求节点
    static int node = -1;
    // 邻接表
    static HashMap<Integer, ArrayList<Integer>> tree;
    public static void main(String[] args) throws IOException {
        BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
        String[] temp = br.readLine().trim().split(" ");
        int n = Integer.parseInt(temp[0]);
        int k = Integer.parseInt(temp[1]);
        temp = br.readLine().trim().split(" ");
        weight = new int[n + 1];
        for(int i = 1; i <= n; i++) weight[i] = Integer.parseInt(temp[i - 1]);
        int x, y;
        // 构建树图的邻接表
        tree = new HashMap<>();
        ArrayList<Integer> list;
        for(int i = 1; i <= n - 1; i++){
            temp = br.readLine().trim().split(" ");
            x = Integer.parseInt(temp[0]);
            y = Integer.parseInt(temp[1]);
            if(tree.containsKey(x)){
                list = tree.get(x);
                list.add(y);
                tree.put(x, list);
            }else{
                list = new ArrayList<>();
                list.add(y);
                tree.put(x, list);
            }
            if(tree.containsKey(y)){
                list = tree.get(y);
                list.add(x);
                tree.put(y, list);
            }else{
                list = new ArrayList<>();
                list.add(x);
                tree.put(y, list);
            }
        }
        int root = Integer.parseInt(br.readLine().trim());
        visited = new boolean[n + 1];
        max = new int[n + 1];
        min = new int[n + 1];
        childNum = new int[n + 1];
        dfs(root, k);
        System.out.println(node);
    }
    
    // 求取节点parent下子节点的最值
    private static void dfs(int parent, int k) {
        visited[parent] = true;
        // 初始化parent下的最值为parent的节点权重
        max[parent] = weight[parent];
        min[parent] = weight[parent];
        // 初始情况下,该子树只有一个节点
        childNum[parent] = 1;
        for(int i = 0; i < tree.get(parent).size(); i++){
            int child = tree.get(parent).get(i);
            if(!visited[child]){
                // 没访问过这个孩子节点就进行深搜
                dfs(child, k);
                max[parent] = Math.max(max[parent], max[child]);
                min[parent] = Math.min(min[parent], min[child]);
                childNum[parent] += childNum[child];
            }
        }
        if(childNum[parent] <= k && max[parent] - min[parent] >= maxDiff){
            // 以parent为根节点的子树满足节点数小于等于k,且最大差值大于等于目前最大
            if(max[parent] - min[parent] > maxDiff){
                // 大于了直接更新,等于的话需要考虑哪个根节点的编号小
                node = parent;
                maxDiff = max[parent] - min[parent];
            }else{
                // 如果node还没有赋值,就直接赋值为当前节点,否则取满足要求的节点中编号最小的
                node = node == -1? parent: Math.min(node, parent);
            }
        }
    }
}

总结

第一题:

  1. 如何得到一个数的各个位数?

    从个位开始不断地对其求对10求余的余数,然后除10,减少位数,直到除了相应的位数次数的10。

    如求六位数的各个位数,就需要从个位开始,将其对10取余、除10,重复六次。

第二题:

  1. 函数参数传递的三种情况:

    1. 将变量名作为实参和形参

      这时传给形参的是变量的值,传递是单向的。如果在执行函数期间形参的值发生变化,并不传回给实参。因为在调用函数时,形参和实参不是同一个存储单元

    2. 传递变量的指针(*)

      形参是指针变量,实参是一个变量的地址,调用函数时,形参(指针变量)指向实参变量单元

      这种虚实结合的方法仍然是“值传递”方式,只是实参的值是变量的地址而已。通过形参指针变量访问主函数中的变量,并改变它们的值。这样就能得到正确结果,但是在概念上却是兜了一个圈子,不那么直截了当。

    3. 传送变量的别名(&)

      实际上,在虚实结合时是把实参的地址传到形参,使形参的地址取实参的地址,从而使它们共享同一单元。同样,将实参的地址传到形参,使形参的地址取实参的地址,从而使它们共享同一单元。这就是地址传递方式

      为便于理解,可以通俗地说:把变量的名字传给引用变量,使引用变量成为变量的别名。

  2. C++ STL中的queue:队列,先进先出

    1. 头文件#include<queue>
    2. 创建queue对象:queue<T> q;
    3. 入队:q.push(x);将x 接到队列的尾端。
    4. 出队:q.pop();弹出队列的第一个元素,注意,并不会返回被弹出元素的值。
    5. 访问队首元素:q.front();即最早被压入队列的元素
    6. 访问队尾元素:q.back();即最后被压入队列的元素。
    7. 判断队列为空:q.empty();当队列空时,返回true。
    8. 返回队列中元素个数:q.size();
  3. ios::sync_with_stdio(false);cin.tie(0)加速C++输入输出流

    1. 之前的总结中已经说过cin/cout比scanf/print慢,因为要先把要输出的东西存入缓冲区,再输出,而使用std::ios::sync_with_stdio(false);这句代码可以来打消iostream的输入输出缓存,节省许多时间,使效率与scanf/printf相差无几。

    2. tie是将两个stream绑定的函数,空参数的话返回当前的输出流指针。这个函数是一个“是否兼容stdio”的开关。C++为了兼容C,保证程序在使用了std::printfstd::cout的时候不发生混乱,将输出流绑到了一起。

      在默认的情况下cin绑定的是cout,每次执行 << 操作符的时候都要调用flush,这样会增加IO负担。可以通过tie(0)(0表示NULL)来解除cincout的绑定,进一步加快执行效率。

第三题:

  1. C++ string获取字符串元素:[]at()

    字符串中元素的访问是允许的,一般可使用两种方法访问字符串中的单一字符:下标操作符[] 和 成员函数at()。两者均返回指定的下标位置的字符。第 1 个字符索引(下标)为 0,最后的字符索引为 length()-1。

  2. 两种访问方法的区别:

    1. 下标操作符[] 在使用时不检查索引的有效性,如果下标超出字符的长度范围,会导致未定义行为。对于常量字符串,使用下标操作符时,字符串的最后字符(即 ‘\0’)是有效的。对应 string 类型对象(常量型)最后一个字符的下标是有效的,调用返回字符 ‘\0’。
    2. 函数 at()在使用时会检查下标是否有效。如果给定的下标超出字符的长度范围,系统会抛出 out_of_range 异常。
  • 0
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值