【笔试题汇总】美团笔试题题解 第三场 2024.3.23

文章讲述了四个编程题目,包括灯光秀中特殊矩形的计数、回文子串删除、玩具排序和路径概率计算,涉及动态规划、贪心算法和概率理论。
摘要由CSDN通过智能技术生成

这里是paoxiaomo,一个现役ACMer,之后将会持续更新算法笔记系列以及笔试题题解系列
本文章面向想打ICPC/蓝桥杯/天梯赛等程序设计竞赛,以及各个大厂笔试的选手
感谢大家的订阅➕ 和 喜欢💗
有什么想看的算法专题可以私信博主

(本文题面由清隆学长收集)

01.K小姐的华丽灯光秀

问题描述

K小姐是一位著名的灯光设计师。她最近正在筹备一场盛大的灯光秀活动。该活动场地由 n n n 排、每排 m m m 个的座位组成。每个座位上都安装有一盏可控灯,能够根据指令点亮或熄灭。

为了营造华丽的视觉效果,K小姐希望在某些阶段让场地中出现一些特殊的 2 × 2 2\times 2 2×2 矩形区域,使得该区域内点亮和熄灭的灯光数量完全相等。她想知道总共有多少种不同的 2 × 2 2\times 2 2×2 矩形区域满足这个要求。

输入格式

第一行包含两个正整数 n n n m m m,分别表示场地的行数和列数。

接下来 n n n 行,每行包含 m m m 个字符,每个字符为 01,表示该座位上的灯光当前状态(0 表示熄灭,1 表示点亮)。

输出格式

输出一个整数,表示满足要求的 2 × 2 2\times 2 2×2 矩形区域的总数。

样例输入

2 3
110
010

样例输出

1

数据范围

  • 2 ≤ n , m ≤ 100 2 \leq n, m \leq 100 2n,m100

【题目解析】

为了满足题目要求,我们需要遍历所有可能的 2 × 2 2\times 2 2×2 矩形区域,并检查每个区域内点亮和熄灭的灯光数量是否相等。

对于每个 2 × 2 2\times 2 2×2 的矩形区域,我们可以将其左上角的坐标设为 ( i , j ) (i, j) (i,j),其中 0 ≤ i ≤ n − 2 0 \leq i \leq n-2 0in2 0 ≤ j ≤ m − 2 0 \leq j \leq m-2 0jm2。然后,我们统计该区域内点亮和熄灭的灯光数量,并检查是否相等。

cpp

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

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

    vector<vector<char>> lights(n, vector<char>(m));

    // 读取灯光状态
    for (int i = 0; i < n; ++i) {
        for (int j = 0; j < m; ++j) {
            cin >> lights[i][j];
        }
    }

    int count = 0;

    // 遍历所有可能的2*2矩形区域
    for (int i = 0; i < n - 1; ++i) {
        for (int j = 0; j < m - 1; ++j) {
            // 统计当前矩形区域内点亮和熄灭的灯光数量
            int light_count = 0;
            int off_count = 0;
            for (int x = i; x <= i + 1; ++x) {
                for (int y = j; y <= j + 1; ++y) {
                    if (lights[x][y] == '1')
                        light_count++;
                    else
                        off_count++;
                }
            }
            // 如果点亮和熄灭的灯光数量相等,则符合要求
            if (light_count == off_count)
                count++;
        }
    }

    cout << count << endl;

    return 0;
}

02.K 小姐的字符串修改

问题描述

K 小姐是一家公司的产品经理,她最近在设计一个新的密码生成系统。为了提高系统的安全性,她决定要求用户在注册时输入一个字符串 s s s,并将其中的某些字符删除,使得最终的字符串不包含长度为偶数的回文子串。

K 小姐希望删除的字符数量尽可能少,因此她想请你帮助她计算出最少需要删除多少个字符。

输入格式

第一行包含一个正整数 n n n,表示字符串 s s s 的长度。

第二行包含一个长度为 n n n 的字符串 s s s

输出格式

输出一个整数,表示最少需要删除的字符数量。

样例输入

5
aaabc

样例输出

2

数据范围

对于所有的测试数据,满足 1 ≤ n ≤ 1 0 5 1 \leq n \leq 10^5 1n105

字符串 s s s 仅由小写英文字母组成。

【题目解析】

我们可以使用贪心的思路来解决这个问题。具体做法是:从左到右扫描字符串,当遇到与前一个字符相同的字符时,就将其删除。这样可以保证最终的字符串中不会出现长度为偶数的回文子串。

例如,对于样例输入 aaabc,我们首先遇到三个连续的 a,那么我们需要删除其中的两个 a,使得剩下的字符串为 abc。这样就不会出现长度为偶数的回文子串了。

时间复杂度为 O ( n ) O(n) O(n),空间复杂度为 O ( 1 ) O(1) O(1),其中 n n n 是字符串 s s s 的长度。

cpp

#include <iostream>
#include <string>

using namespace std;

int main() {
    int n;
    cin >> n;
    string s;
    cin >> s;
    char prev = ' ';
    int ans = 0;
    for (char c : s) {
        if (c == prev) {
            ans++;
        }
        prev = c;
    }
    cout << ans << endl;
    return 0;
}

03.A 先生的玩具摆放

问题描述

A 先生是一位资深的玩具收藏家,他有一个长度为 n n n 的玩具架,每个位置上都摆放着一个玩具。A 先生的玩具分为红色和蓝色两种,他希望将所有同色玩具摆放在一起,以方便欣赏。

A 先生可以交换任意两个红色玩具的位置,但不能移动蓝色玩具。他希望在尽可能少的交换次数下,使得玩具架上的玩具按颜色排序,即红色玩具在前,蓝色玩具在后。请你帮助 A 先生计算出最少需要交换多少次。

输入格式

第一行包含一个正整数 n n n,表示玩具架的长度。

第二行包含 n n n 个正整数 a 1 , a 2 , … , a n a_1, a_2, \ldots, a_n a1,a2,,an,表示每个位置上玩具的编号。

第三行包含一个长度为 n n n 的字符串,每个字符表示对应位置上玩具的颜色,用 R 表示红色,用 B 表示蓝色。

输出格式

输出一个整数,表示最少需要交换的次数。如果无法完成排序,输出 -1

样例输入

4
1 3 2 4
BRRB

样例输出

1

数据范围

对于所有的测试数据,满足 1 ≤ n ≤ 1 0 5 1 \leq n \leq 10^5 1n105

玩具编号均为正整数,且互不相同。

【题目解析】

我们可以使用贪心的思路来解决这个问题。具体做法是:从左到右扫描玩具架,对于每个红色玩具,如果它的位置不正确,那么我们就尝试将它与正确位置上的红色玩具交换。如果正确位置上是蓝色玩具,那么就无法完成排序,输出 -1

例如,对于样例输入 1 3 2 4, BRRB,我们首先扫描到第一个红色玩具 3,它应该放在第二个位置,所以我们将它与第二个位置上的玩具 2 交换一次。此时玩具架变为 1 2 3 4, BRBR,满足要求。

时间复杂度为 O ( n ) O(n) O(n),空间复杂度为 O ( n ) O(n) O(n),其中 n n n 是玩具架的长度。

cpp

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

int main() {
 int n;
 cin >> n;
 vector<int> nums(n);
 for (int i = 0; i < n; i++) {
 cin >> nums[i];
 }
 string colors;
 cin >> colors;
 int redIdx = 0;
 int ans = 0;
 for (int i = 0; i < n; i++) {
 if (colors[i] == 'R') {
 while (redIdx < n && colors[redIdx] != 'R') {
 redIdx++;
 }
 if (redIdx == n) {
 cout << -1 << endl;
 return 0;
 }
 if (nums[i] != nums[redIdx]) {
 swap(nums[i], nums[redIdx]);
 ans++;
 }
 redIdx++;
 }
 }
 cout << ans << endl;
 return 0;
}

04.K小姐的密码设计

LYA 小姐的花园整理

问题描述

LYA 小姐是一位资深的园艺爱好者,她拥有一个巨大的花园。花园中种植了许多不同种类的花卉,分布在不同的区域。为了便于管理和赏花,LYA 小姐希望将花园分割成若干个连续的区域,使得每个区域内花卉的丰富度不小于一个给定的阈值 k k k

我们定义一个区域内花卉的丰富度为:该区域内不同花卉种类的数量乘以该区域的面积。例如,一个区域面积为 6 6 6 平方米,种植了矢车菊、玫瑰和百合三种花卉,那么该区域的丰富度就是 3 × 6 = 18 3 \times 6 = 18 3×6=18

请你帮助 LYA 小姐计算出,最多能将花园分割成多少个满足要求的区域。

输入格式

第一行包含两个正整数 n n n k k k,分别表示花园的总面积(单位:平方米)和最小丰富度要求。

第二行是一个字符串,按顺序给出了花园中每一段区域的面积和种植的花卉种类,其中:

  • 小写字母表示该区域种植的花卉种类
  • 括号内的数字表示该种花卉所占的区域面积

例如,a(2)b(3)c(1)表示该区域总面积为 6 6 6 平方米,前 2 2 2 平方米种植了 a 种花卉,接着 3 3 3 平方米种植了 b 种花卉,最后 1 1 1 平方米种植了 c 种花卉。

输出格式

如果无法将花园分割成任何一个满足要求的区域,请输出 -1。否则,输出最多能分割出的区域数量。

样例输入 1


7 6
a(2)b(2)a(3)

样例输出 1


2

样例输入 2


1000000000 5
x(1000000000)

样例输出 2


200000000

数据范围

  • 对于所有数据,满足 1 ≤ k , n ≤ 1 0 18 1 \leq k, n \leq 10^{18} 1k,n1018
  • 输入字符串长度不超过 1 0 6 10^6 106,只包含小写字母、数字和括号

【题目解析】

我们可以使用贪心和二分查找的思路来解决这个问题。具体做法如下:

  1. 首先遍历输入字符串,将连续的相同花卉种类合并,得到一个列表 a。例如,对于输入 a(2)b(3)a(1),我们将得到 a = [3, 3],表示前 3 3 3 平方米种植了第一种花卉,后 3 3 3 平方米种植了第二种花卉。

  2. 定义三个变量 res,cnt,lens 分别表示区域数量、当前区域内不同花卉种类数和当前区域面积。初始值分别为 0 , 0 , 0 0,0,0 0,0,0

  3. 遍历列表 a,对于每个元素 v:

    • 如果将它加入当前区域后,区域丰富度仍然小于 k k k,那么就将它加入当前区域,更新 cntlens
    • 否则,我们需要从当前区域切出一个新区域。使用二分查找,找出最大的 x x x,使得 (cnt + 1) * (lens + x) < k
      • res 1 1 1,表示新增一个区域。
      • cntlens 更新为 1 1 1v - x$,表示新区域只有一种花卉,面积为 v - x`。
      • 如果 lens >= k,那么可以从新区域中再切出 lens // k 个满足要求的区域,将 res 加上 lens // k,并将 lens 更新为 lens % k
      • 如果 lens 0 0 0,就将 cnt 置为 0 0 0,否则置为 1 1 1
  4. 如果 res 0 0 0,说明无法分割出任何满足要求的区域,输出 -1。否则输出 res

cpp

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

int main() {
    long long n, k;
    cin >> n >> k;
    string s;
    cin >> s;

    // 合并连续相同花卉种类
    vector<long long> a;
    char last = 0;
    long long cnt = 0;
    for (char c : s) {
        if (isalpha(c)) {
            if (c != last) {
                a.push_back(cnt);
                cnt = 0;
                last = c;
            }
            cnt++;
        } else {
            cnt *= stoll(string(1, c));
        }
    }
    a.push_back(cnt);

    // 贪心+二分查找
    long long res = 0, curCnt = 0, curLen = 0;
    for (long long length : a) {
        if ((curCnt + 1) * (curLen + length) < k) {
            curCnt += 1;
            curLen += length;
        } else {
            long long l = 1, r = length;
            while (l < r) {
                long long mid = (l + r) / 2;
                if ((curCnt + 1) * (curLen + mid) < k) {
                    l = mid + 1;
                } else {
                    r = mid;
                }
            }
            res += 1;
            curCnt = 1;
            curLen = length - l + 1;
            if (curLen >= k) {
                res += curLen / k;
                curLen %= k;
            }
            curCnt = curLen == 0 ? 0 : 1;
        }
    }
    cout << (res == 0 ? -1 : res) << endl;
    return 0;
}

05.K小姐的树林漫步

问题描述

K小姐在一座充满魔法的树林中漫步,每次她都会选择一条新的路径,以探索这片未知的森林。树林中的每个节点都有其独特的风景,现在K小姐想要从节点 s s s 前往节点 t t t,欣赏不同的景致。由于路径选择的多样性,她希望知道,能够成功抵达目的地的概率是多少。

由于树林的神秘性,她可能会进行多次询问。每次询问将会给出她的起始点和目的地,请计算并输出她成功到达目的地的概率。概率结果需要取模 1 0 9 + 7 10^9+7 109+7 后返回。假设答案为分数 a b \frac{a}{b} ba a , b a, b a,b 互质),您需要找出一个整数 x x x 使得 b ⋅ x ≡ a ( m o d 1 0 9 + 7 ) b \cdot x \equiv a \pmod{10^9+7} bxa(mod109+7),且 0 ≤ x < 1 0 9 + 7 0 \le x < 10^9+7 0x<109+7

输入格式

第一行包含一个正整数 n n n,表示树林中的节点个数。

接下来的 n − 1 n-1 n1 行,每行包含两个整数 u , v u, v u,v,代表树林中的一条路径。

之后一行包含一个正整数 q q q,代表询问的次数。

接下来的 q q q 行,每行包含两个整数 s , t s, t s,t,表示每次询问的起始节点和目的地。

输出格式

输出包含 q q q 行,每行包含一个整数,代表对应询问的概率取模 1 0 9 + 7 10^9+7 109+7 的结果。

样例输入

3
1 2
1 3
2
2 3
1 3

样例输出

1
500000004

数据范围

  • 对于 100 % 100\% 100% 的数据,保证 1 ≤ n , q ≤ 2 × 1 0 5 1 \le n, q \le 2 \times 10^5 1n,q2×105
  • 节点编号从 1 1 1 n n n

【题目解析】

这个问题可以看作是在一棵树上进行路径计数的问题。给定一棵树和两个节点,需要计算从起始节点到目标节点的路径数目。
考虑到树的特性,我们可以使用动态规划来解决这个问题。我们可以从树的根节点开始,自底向上计算每个节点的子树中到达目标节点的路径数目,然后将这些信息传递到父节点,最终得到整棵树的路径数目。

  1. 使用邻接表构建树的数据结构,记录每个节点的相邻节点。
  2. 使用动态规划,自底向上计算每个节点的子树中到达目标节点的路径数目。
  3. 对于每个询问,根据预先计算好的路径数目,计算起始节点到目标节点的路径数目,并取模 1 0 9 + 7 10^9+7 109+7 后输出。

cpp

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

const int MOD = 1000000007;

void dfs(int node, int parent, const vector<vector<int>>& tree, vector<int>& sub_paths, vector<int>& total_paths) {
    sub_paths[node] = 1;
    for (int neighbor : tree[node]) {
        if (neighbor != parent) {
            dfs(neighbor, node, tree, sub_paths, total_paths);
            sub_paths[node] = (sub_paths[node] + sub_paths[neighbor]) % MOD;
            total_paths[node] = (total_paths[node] + total_paths[neighbor]) % MOD;
        }
    }
    total_paths[node] = (total_paths[node] + sub_paths[node]) % MOD;
}

int main() {
    int n;
    cin >> n;

    vector<vector<int>> tree(n + 1);
    for (int i = 0; i < n - 1; ++i) {
        int u, v;
        cin >> u >> v;
        tree[u].push_back(v);
        tree[v].push_back(u);
    }

    vector<int> sub_paths(n + 1, 0); // 子树中到达目标节点的路径数目
    vector<int> total_paths(n + 1, 0); // 整棵树中到达目标节点的路径数目

    dfs(1, 0, tree, sub_paths, total_paths);

    int q;
    cin >> q;
    while (q--) {
        int s, t;
        cin >> s >> t;
        // 对于每个询问,计算起始节点到目标节点的路径数目,并取模后输出
        int paths = (total_paths[s] - sub_paths[t] + MOD) % MOD;
        cout << paths << endl;
    }

    return 0;
}

整理试题不易,你的关注是我更新的最大动力!关注博主 带你看更多面试及竞赛试题和实用算法。

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值