0
点赞
收藏
分享

微信扫一扫

【蓝桥杯预备营集结五】第十三届蓝桥杯模拟赛 C/C++ 试题分析及解答

💗 大家好🤗🤗🤗,我是左手の明天!💗

📆 最近更新:2022 年 4 月 11 日,左手の明天的第 224 篇原创博客


🚩试题A

⭐️问题描述

请问在 10000 (含)到 90000 (含)中,有多少个数是 128 的倍数。

⭐️方法⼀:遍历

#include <iostream>
using namespace std;
int main() {
    int ans = 0;
    for (int i = 10000; i <= 90000; i++) {
        if (i % 128 == 0) ans++;
    }
    cout << ans << endl;
    return 0;
}

⭐️方法二:直接计算

#include <iostream>
using namespace std;
int main() {
    int a = 10000, b = 90000;
    int ans = (b - a) / 128 + (a % 128 > b % 128 || a % 128 == 0);
    cout << ans << endl;
    return 0;
}

⭐️结果


🚩试题B

⭐️问题描述

2021 是⼀个特殊的年份,它的千位和⼗位相同,个位⽐百位多⼀。请问从 1000 (含)到 9999 (含)有多少个这样的年份?

⭐️方法⼀:遍历

#include <iostream>
using namespace std;
int main() {
    int ans = 0;
    for (int i = 1000; i <= 9999; i++) {
        int a = i / 1000, b = (i / 100) % 10, c = (i / 10) % 10, d = i % 10;
        if (a == c && d == b + 1) ans++;
    }
    cout << ans << endl;
    return 0;
}

⭐️方法二:直接计算

在千位确定时,⼗位与千位相同,个位⽐百位⼤⼀的可能有 9 种
从 1000 到 9999 有 9 个不同的千位
所以答案是 9 * 9 = 81

⭐️结果


🚩试题C

⭐️问题描述

如果 1 到 n 的⼀个排列中,所有奇数都在原来的位置上,称为⼀个奇不动排列。

例如,1 到 7 的排列 (1, 6, 3, 4, 5, 2, 7)是⼀个奇不动排列,因为1, 3, 5, 7都在原来的位置上。请问,1 到 21 的所有排列中,有多少个奇不动排列。

⭐️方法

1 ~ 21 之间有 10 个偶数,奇数的位置不变,10 个偶数在 10 个位置上的全排列就是答案,即 10!

#include <iostream>
using namespace std;
int main() {
    int ans = 1;
    for (int i = 1; i <= 10; i++){
        ans *= i;
    }
    cout << ans << endl;
    return 0;
}

⭐️结果


🚩试题D

⭐️问题描述

小蓝要上⼀个楼梯,共 15 级台阶。小蓝每步可以上 1 级台阶,也可以上 2 级、3 级或 4 级,再多就没办法⼀步⾛到了。每级台阶上有⼀个数值,可能正也可能负。每次⾛到⼀级台阶上,⼩蓝的得分就加上这级台阶上的数值。台阶上的数值依次为: 1, 2, 1, 1,1, 1, 5, 5, 4, -1, -1, -2, -3, -1, -9。

小蓝希望不超过 6 步⾛到台阶顶端,请问他得分的最⼤值是多少?

注意,小蓝开始站在地⾯上,地⾯没有分值。他最终要⾛到台阶顶端,所以最终⼀定会⾛到数值为 -9 的那级台阶,所以 -9 ⼀定会加到得分里⾯。

⭐️题解:深搜

#include <iostream>
using namespace std;

const int INF = 0x3f3f3f3f;
int a[16] = {0, 1, 2, 1, 1, 1, 1, 5, 5, 4, -1, -1, -2, -3, -1, -9};

int dfs(int idx, int k) {
    if (idx == 15) return a[15];
    if (k == 6) return -INF;
    int ans = -INF;
    for (int i = 1; i <= 4; i++) {
        ans = max(ans, dfs(idx + i, k + 1));
    }
    return ans + a[idx];
}

int main() {
    cout << dfs(0, 0) << endl;
    return 0;
}

⭐️结果


🚩试题E

⭐️问题描述

在数列 a[1], a[2] … a[n] 中,如果对于下标 i, j, k 满⾜ 0 < i < j < k < n + 1 且 a[i] < a[j] < a[k], 则称 a[i], a[j], a[k] 为⼀组递增三元组。

请问对于下⾯的长度为20的数列,有多少个递增三元组?

2, 9, 17, 4, 14, 10, 25, 26, 11, 14, 16, 17, 14, 21, 16, 27, 32, 20, 26, 36。

⭐️题解:枚举

#include <iostream>
using namespace std;

int a[20] = {2, 9, 17, 4, 14, 10, 25, 26, 11, 14, 16, 17, 14, 21, 16, 27, 32, 20, 26, 36};

int main() {
    int ans = 0;
    int n = 20;
    for (int i = 0; i < n; i++) {
        for (int j = i + 1; j < n; j++) {
            for (int k = j + 1; k < n; k++) {
                if (a[i] < a[j] && a[j] < a[k]) ans++;
            }
        }
    }
    cout << ans << endl;
    return 0;
}

⭐️结果


🚩试题F

⭐️问题描述

有⼀个n * m 的棋盘,现在对这个棋盘进行黑白染色,左上⾓染成黑色。从左上角开始,每个黑色格的相邻格染成白色,白色格的相邻格染成黑色。以下给出了⼀个5 * 7的棋盘的染色示例。

(可以理解为已经被染色的格⼦不会再次被染色)

给定 n 和 m,请问棋盘上⼀共有多少方格被染成了黑色。

【输入格式】

输⼊⼀行包含两个整数n, m,⽤⼀个空格分隔。

【输出格式】

输出⼀行包含—个整数,表示答案。

【样例输入】

5 7

【样例输出】

18

【评测⽤例规模与约定】

对于50%的评测用例,1 <= n, m <= 10000,
对于所有评测用例,1 <= n, m <= 1000000。

⭐️方法一:逐行计数

对于每行的格子来说,黑色与白色是被交替染色的,若第 1 列是黑色,那么该行黑色格⼦数是奇数列数,否则就是偶数列数,也可知奇数行是黑色开头(行与列从 1 开始数)

可以使⽤⼀个布尔值,交替表示当前行是奇数行还是偶数行

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

typedef long long ll;

int main() {
    ll m, n, ans = 0;
    bool f = true;
    cin >> m >> n;
    if (m > n) swap(m, n); //m,n可以互换,结果不变
    for (int i = 0; i < m; i++) {
        ans += (n + f) / 2; //(n+1)/2表⽰1~n之间的奇数个数
        f = !f;
    }
    cout << ans << endl;
    return 0;
}

⭐️方法⼆:直接计算

可以发现当⾏号与列号相加为偶数是,该格子是黑色,此时有两种情况:行号列号都是奇数,或者⾏号列号都是偶数,所以答案是:奇行个数 × 奇列个数 + 偶行个数 × 偶列个数 (行与列从 1 开始数)

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

typedef long long ll;

int main() {
    ll m, n, ans = 0;
    cin >> m >> n;
    ans += ((m + 1) / 2) * ((n + 1) / 2);
    ans += (m / 2) * (n / 2); //涉及到取整问题,括号不能少
    cout << ans << endl;
    return 0;
}

🚩试题G

⭐️问题描述

小蓝要在路边划分停车位。他将路边可停车的区域划分为 L个整数小块,编号1至L。⼀个车位需要连续的 K 个小块,停车位不能重叠。有的小块属于井盖、消防通道等区域,不能停车。

请问小蓝最多划分出多少个停车位?

【输⼊格式】

输入的第⼀行包含三个整数 L、K、N,分别表示小块数量、⼀个车位需要的小块数量和不能停车的小块数量,相邻整数之间用⼀个空格分隔。

第⼆⾏包含 N 个整数a[1], a[2], …, a[n],按从小到⼤的顺序排列,相邻的整数间用空格分隔,表示这些位置不能停车。

【输出格式】

输出⼀行包含⼀个整数,表示答案。

【样例输⼊】

100 10 2
25 91

【样例输出】

8

⭐️题解

注意输入格式,第⼆行的输入会按从小到⼤的顺序排列

#include <iostream>
using namespace std;
int main() {
    int len, k, n;
    cin >> len >> k >> n;
    int ans = 0, last = 0, tmp;
    for (int i = 0; i < n; i++) {
        cin >> tmp;
        ans += (tmp - last - 1) / k;
        last = tmp;
    }
    ans += (len + 1 - last - 1) / k;
    cout << ans << endl;
    return 0;
}

🚩试题 H

⭐️问题描述

⼀个 1 到 n 的排列被称为⼈字排列,是指排列中的第 1 到第 (n + 1) / 2 个元素单调递增,第 (n + 1) / 2 到第 n 个元素单调递减。

例如: (2, 3, 5, 8, 9, 7, 6, 4, 1)是⼀个⼈字排列,⽽ (1, 2, 3) 和 (2, 1, 3) 都不是⼈字排列,(2, 4, 3) 也不是⼀个⼈字排列 (它甚⾄不是⼀个 1 到 4 的排列) 。

请问,1 到 n 的排列中有多少个⼈字排列?

【输⼊格式】

输⼊⼀⾏包含⼀个奇数 n。

【输出格式】

输出⼀⾏包含⼀个整数,表⽰答案,答案可能很⼤,请输出答案除以1000000007的余数。

【样例输⼊】

5

【样例输出】

6

⭐️题解

注意输⼊的 n ⼀定为奇数

最⼤的数⼀定固定在中间位置,再从剩下的 (n - 1) 个数中选出 (n / 2) 个数升序排列放在左边,剩下的 (n / 2) 个数降序排列放在右边,所以答案是 C(n - 1, n / 2)

#include <iostream>
using namespace std;

const int MOD = 1E9 + 7;

//求从a中取b个的组合数
int getc(int a, int b) {
    b = min(b, a - b);
    long long ans = 1;
    for (int i = 0; i < b; i++) {
        ans = (ans * (a - i) / (i + 1)) % MOD;
    }
    return ans;
}

int main() {
    int n;
    cin >> n;
    cout << getc(n - 1, n / 2) << endl;
    return 0;
}

🚩试题I

⭐️问题描述

著名设计师⼩蓝给蓝桥⼩学设计了⼀个教学楼。蓝桥⼩学经常下⾬,所以校长希望教学楼任何地⽅都可以连通到其它地⽅。⼩蓝给出了教学楼的平⾯图,⽤⼀个 n ⾏ m 列的 01 矩阵表⽰,其中 0 表⽰空地,1 表⽰教学楼。两个相邻的 1 (上下相邻或左右相邻)之间互相可达。

请帮⼩蓝检查⼀下,是否教学楼的任意两个地⽅都可以连通到其它地⽅。

【输⼊格式】

输⼊的第⼀⾏包含两个整数 n, m,⽤⼀个空格分隔。接下来n⾏,每⾏⼀个长度为 m 的 01 串,表⽰教学楼的平⾯图。

【输出格式】

如果满⾜要求,输出“YES”,否则输出“NO”,请注意字母全部都是⼤写。

⭐️方法⼀:广搜

题解中将 m 与 n 互换了,m 表⽰⾏,n 表⽰列

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

int m, n;
vector<string> mp;
int a[][2] = {{1, 0}, {-1, 0}, {0, 1}, {0, -1}};

bool find1(int &x, int &y) {
    for (int i = 0; i < m; i++) {
        for (int j = 0; j < n; j++) {
            if (mp[i][j] == '1') {
                x = i, y = j;
                return true;
            }
        }
    }
    return false;
}

int main() {
    cin >> m >> n;
    mp.resize(m);
    for (int i = 0; i < m; i++) {
        cin >> mp[i];
    }
    int x, y;
    if (!find1(x, y)) {
        cout << "TES" << endl;
        return 0;
    }
    queue<pair<int, int> > q;
    mp[x][y] = '0';
    q.push(make_pair(x, y));
    while (!q.empty()) {
        x = q.front().first;
        y = q.front().second;
        q.pop();
        for (int i = 0; i < 4; i++) {
            int xx = x + a[i][0];
            int yy = y + a[i][1];
            if (xx >= 0 && xx < m && yy >= 0 && yy < n && mp[xx][yy] == '1') {
                mp[xx][yy] = '0';
                q.push(make_pair(xx, yy));
            }
        }
    }
    if (find1(x, y)) cout << "NO" << endl;
    else cout << "YES" << endl;
    return 0;
}

也可⽤ cnt 记录矩阵中 ‘1’ 的个数,每放⼊队列⼀个坐标,就令 cnt -= 1,最后判断 cnt 是否等于 0 即可,如在⽅法⼆深搜中,每递归⼀次,就将 cnt -= 1

⭐️方法二:深搜

深搜有爆栈的风险,这样的题还是该⼴搜⽐较好……

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

int m, n, cnt = 0;
vector<string> mp;
int a[][2] = {{1, 0}, {-1, 0}, {0, 1}, {0, -1}};

bool find1(int &x, int &y) {
    for (int i = 0; i < m; i++) {
        for (int j = 0; j < n; j++) {
            if (mp[i][j] == '1') {
                if (cnt == 0) x = i, y = j;
                cnt++;
            }
        }
    }
    return cnt;
}

void dfs(int x, int y) {
    for (int i = 0; i < 4; i++) {
        int xx = x + a[i][0];
        int yy = y + a[i][1];
        if (xx >= 0 && xx < m && yy >= 0 && yy < n && mp[xx][yy] == '1') {
            mp[xx][yy] = '0';
            cnt--;
            dfs(xx, yy);
        }
    }
}

int main() {
    cin >> m >> n;
    mp.resize(m);
    for (int i = 0; i < m; i++) {
        cin >> mp[i];
    }
    queue<pair<int, int> > q;
    int x, y;
    if (!find1(x, y)) {
        cout << "TES" << endl;
        return 0;
    }
    cnt--;
    mp[x][y] = '0';
    dfs(x, y);
    if (cnt) cout << "NO" << endl;
    else cout << "YES" << endl;
    return 0;
}

🚩试题J

⭐️问题描述

如果⼀个整数 g 能同时整除整数 A 和 B,则称 g 是 A 和 B 的公约数。例如:43 是 86 和 2021 的公约数。

请问在 1(含) 到 2021(含) 中,有多少个数与 2021 存在⼤于 1 的公约数。请注意 2021 和 2021 有⼤于 1 的公约数,因此在计算的时候要算⼀个。

⭐️题解:枚举

#include <iostream>
using namespace std;

int gcd(int a, int b) {
    return (a % b == 0) ? b : gcd(b, a % b);
}

int main() {
    int ans = 0;
    for (int i = 1; i <= 2021; i++) {
        if (gcd(2021, i) > 1) ans++;
    }
    cout << ans << endl;
    return 0;
}

⭐️结果


🚩试题K

⭐️问题描述

2021 是⼀个⾮常特殊的数,它可以表⽰成两个⾮负整数的平⽅差,2021 = 45 * 45 - 2 * 2。
2025 也是同样特殊的数,它可以表⽰成 2025 = 45 * 45 - 0 * 0。

请问,在 1 到 2021 中有多少个这样的数?

请注意,有的数有多种表⽰⽅法,例如 9 = 3 * 3 - 0 * 0 = 5 * 5 - 4 * 4,在算答案时只算⼀次

⭐️方法⼀:枚举

假设有: x = a^2 - b^2 = (a + b) * (a - b)

可知 (a + b) 和 (a - b) 是 x 的⼀对约数

反过来,要判断 x 是否符合条件可以找出 x 的每⼀对约数,判断两约数是否能变成 (a + b) * (a - b)的形式,并且 a 和 b 都是正整数

因为 (a + b) + (a - b) = 2a (a + b) - (a - b) = 2b

所以判断这⼀对约数的和与差是否为偶数即可

若两数之和为偶数,那么两数之差也为偶数,所以两者判断其⼀即可

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

bool check(int x) {
    int n = sqrt(x);
    for (int i = 1; i <= n; i++) {
        if (x % i == 0 && (i + x / i) % 2 == 0) {
            return true;;
        }
    }
    return false;
}

int main() {
    int ans = 0;
    for (int i = 1; i <= 2021; i++) {
        ans += check(i);
    }
    cout << ans << endl;
    return 0;
}

⭐️方法二:枚举因数

⽅法⼀是为判断某个数是否是特别的数,⽅法⼆是枚举出特别的数,判断是否在 1 ~ 2021 范围内
令 i 代替 (a + n),j 代替 (a - b) 枚举,若 (i + j) 是偶数并且 (i * j) 在范围内就打上标记,最后查有多少个数被标记

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

int main() {
    int a[2022] = {0};
    for (int i = 1; i <= 2021; i++) {
        for (int j = 1; j <= i; j++) {
            if ((i + j) % 2 == 0 && i * j <= 2021) {
                a[i * j] = 1;
            }
        }
    }
    int ans = 0;
    for (int i = 1; i <= 2021; i++) {
        ans += a[i];
    }
    cout << ans << endl;
    return 0;
}

⭐️结果


🚩试题L

⭐️问题描述

⼩蓝要⽤01串来表达⼀段⽂字,这段⽂字包含 a, b, c, d, e, f 共 6 个字母,每个字母出现的次数依次为:a 出现 10 次,b 出现 20 次,c出现 3 次,d 出现 4 次,e 出现 18 次,f 出现 50 次。⼩蓝准备分别对每个字母使⽤确定的 01 串来表⽰,不同字母的 01 串长度可以不相同。

在表⽰⽂字时,将每个字母对应的 01 串直接连接起来组成最终的 01 串。为了能够正常还原出⽂字,⼩蓝的编码必须是前缀码,即任何⼀个字符对应的 01 串都不能是另⼀个字符对应的 01 串的前缀。

例如,以下是⼀个有效的编码:

a: 000
b: 111
c: 01
d: 001
e: 110
f: 100

其中 c 的长度为 2,其它字母的编码长度为 3,这种⽅式表⽰这段⽂字需要的总长度为:103 + 203 + 32 + 43 + 183 + 503 = 312。

上⾯的编码显然不是最优的,将上⾯的 f 的编码改为 10,仍然满⾜条件,但是总长度为 262,要短 50。

要想编码后的总长度尽量⼩,应当让出现次数多的字符对应的编码短,出现次数少的字符对应的编码长。

请问,在最优情况下,编码后的总长度最少是多少?

⭐️题解:哈夫曼编码

将每个字符出现的次数作为权值,每次取出最⼩的两个权值,作为⼆叉树的左右节点,再将权值之和作为⽗节点,放回队列中,循环此过程构建哈夫曼树

⾃⼆叉树根节点开始,向左为 0,向右为 1,得出每个字符的编码,结果如下:

左右⼦节点的位置不同,会使编码结果不同,但位数相同
a: 1101(4位) b: 10(2位) c: 11000(5位)
d: 11001(5位) e: 111(3位) f: 0(1位)
所以答案为: 4*10 + 2*20 + 5*3 + 5*4 + 3*18 + 1*50 = 219

🚩试题M

⭐️问题描述

下⾯的矩阵中包含 ABCDEF 六种字符,请问出现最多的字符出现了几次?

FFEEFEAAECFFBDBFBCDA
DACDEEDCCFFAFADEFBBA
FDCDDCDBFEFCEDDBFDBE
EFCAAEECEECDCDECADDC
DFAEACECFEADCBFECADF
DFBAAADCFAFFCEADFDDA
EAFAFFDEFECEDEEEDFBD
BFDDFFBCFACECEDCAFAF
EFAFCDBDCCBCCEADADAE
BAFBACACBFCBABFDAFBE
FCFDCFBCEDCEAFBCDBDD
BDEFCAAAACCFFCBBAAEE
CFEFCFDEEDCACDACECFF
BAAAFACDBFFAEFFCCCDB
FADDDBEBCBEEDDECFAFF
CDEAFBCBBCBAEDFDBEBB
BBABBFDECBCEFAABCBCF
FBDBACCFFABEAEBEACBB
DCBCCFADDCACFDEDECCC
BFAFCBFECAACAFBCFBAF

⭐️题解:哈希表

⽤数组代替哈希映射即可

//⽤题⽬中的矩阵作为输⼊可得出答案

#include <iostream>
using namespace std;

int mp[200], idx = 0;
string str;

int main() {
    for (int i = 0; i < 20; i++) {
        cin >> str;
        for (int j = 0; j < str.length(); j++) {
            mp[str[j]]++;
        }
    }
    for (int i = 0; i < 200; i++) {
        if (mp[i]) {
            cout << char(i) << " " << mp[i] << endl;
            if (mp[i] > mp[idx]) idx = i;
        }
    }
    cout << "ans = " << mp[idx] << endl;
    return 0;
}

⭐️结果


🚩试题N

⭐️问题描述

⼩蓝要到店⾥买铅笔。铅笔必须⼀整盒⼀整盒买,⼀整盒 12 ⽀,价格 p 元。⼩蓝⾄少要买 t ⽀铅笔,请问他最少花多少钱?

【输⼊格式】

输⼊⼀⾏包含两个整数 p、t,⽤⼀个空格分隔。

【输出格式】

输出⼀⾏包含—个整数,表⽰答案。

【样例输⼊】

5 30

【样例输出】

15

【评测⽤例规模与约定】

对于所有评测⽤例, 1 <= p <= 100,1 <= v <= 10000。

⭐️题解

t / 12 表⽰盒数,若 t % 12 不为 0 就多买⼀盒

#include <iostream>
using namespace std;
int main() {
    int p, t;
    cin >> p >> t;
    int k = (t / 12) + bool(t % 12);
    cout << k * p << endl;
    return 0;
}

🚩试题O

⭐️问题描述

给定⼀个三⾓形的三条边的长度 a, b, c,请问这个三⾓形是不是⼀个直⾓三⾓形。

【输⼊格式】

输⼊⼀⾏包含三个整数 a, b, c,表⽰三⾓形三边的长度,相邻整数之间⽤⼀个空格分隔。

【输出格式】

如果是直⾓三⾓形,输出 “YES”(全⼤写),否则输出 “NO”(全⼤写)。

【样例输⼊】

3 4 5

【样例输出】

YES

【样例输⼊】

4 5 4

【样例输出】

NO

⭐️题解

排序,最⼩的两个数的平⽅和等于最⼤数的平⽅,就是直⾓三⾓形

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

int a[3];

bool check() {
    return a[0] * a[0] + a[1] * a[1] == a[2] * a[2];
}

int main() {
    for (int i = 0; i < 3; i++) {
        cin >> a[i];
    }
    sort(a, a + 3);
    if (check()) cout << "YES" << endl;
    else cout << "NO" << endl;
    return 0;
}

🚩试题P

⭐️问题描述

n 个⼩朋友正在做⼀个游戏,每个⼈要分享⼀个⾃⼰的⼩秘密。每个⼩朋友都有⼀个 1 到 n 的编号,编号不重复。为了让这个游戏更有趣,⽼师给每个⼩朋友发了⼀张卡⽚,上⾯有⼀个 1 到 n 的数字,每个数字正好出现⼀次。每个⼩朋友都将⾃⼰的秘密写在纸上,然后根据⽼师发的卡⽚上的数字将秘密传递给对应编号的⼩朋友。如果⽼师发给⾃⼰的数字正好是⾃⼰的编号,这个秘密就留在⾃⼰⼿⾥。⼩朋友们拿到其他⼈的秘密后会记下这个秘密,⽼师会再指挥所有⼩朋友将⼿中的秘密继续传递,仍然根据⽼师发的卡⽚上的数字将秘密传递给对应编号的⼩朋友。这样不断重复 n 次。现在,每个⼩朋友都记下了很多个秘密。⽼师现在想找⼀些⼩朋友,能说出所有秘密,请问⽼师最少要找⼏个⼩朋友?

【输⼊格式】

输⼊的第⼀⾏包含⼀个整数 n。
第⼆⾏包含 n 个整数 a[1], a[2], …, a[n],相邻的整数间⽤空格分隔,分别表⽰编号 1 到 n 的⼩朋友收到的数字。

【输出格式】

输出⼀⾏包含⼀个整数,表⽰答案。

【样例输⼊】

6
2 1 3 5 6 4

【样例输出】

8

【样例说明】

最终⼩朋友 1, 2 互相知道了对⽅的秘密,⼩朋友 3 只知道⾃⼰的秘密,⼩朋友 4, 5, 6 互相知道了对⽅的秘密。⾄少要找 3 个⼩朋友才能说出所有秘密。

【评测⽤例规模与约定】

对于 30% 的评测⽤例,2 <= n <= 30。
对于 60% 的评测⽤例,2 <= n <= 1000。
对于所有评测⽤例,2 <= n <= 100000。

⭐️方法⼀:广搜

由于卡⽚不会重复,所以答案就是环的个数

因为经过 n 轮循环后,环中的每个⼩朋友都知道环中所有⼈的秘密,所以从每个环中找出⼀⼈即可

#include <iostream>
using namespace std;

const int MAXN = 100005;
int n, tmp, ans = 0;;
int a[MAXN];

int main() {
    // freopen("input.txt", "r", stdin);
    cin >> n;
    for (int i = 1; i <= n; i++) {
        cin >> a[i];
    }
    for (int i = 1; i <= n; i++) {
        if (a[i] != 0) ans++;
        int idx = i;
        while (a[idx]) {
            tmp = a[idx];
            a[idx] = 0;
            idx = tmp;
        }
    }
    cout << ans << endl;
    return 0;
}

⭐️方法二:并查集

本该是道并查集的题,但是数据范围太⼤,时间复杂度不稳定,极限数据时递归会爆栈,只能过 60% 的样例

#include <iostream>
using namespace std;

const int MAXN = 100005;
int n, tmp, ans = 0;;
int a[MAXN];

int findf(int idx) {
    if (a[idx] == 0 || a[idx] == idx) return idx;
    a[idx] = findf(a[idx]);
    return a[idx];
}

int main() {
    cin >> n;
    for (int i = 1; i <= n; i++) {
        cin >> tmp;
        a[i] = findf(tmp);
    }
    for (int i = 1; i <= n; i++) {
        if (a[i] == i) ans++;
    }
    cout << ans << endl;
    return 0;
}

🚩试题Q

⭐️问题描述

⼀个 1 到 n 的排列被称为半递增序列,是指排列中的奇数位置上的值单调递增,偶数位置上的值也单调递增。

例如:(1, 2, 4, 3, 5, 7, 6, 8, 9) 是⼀个半递增序列,因为它的奇数位置上的值是 1, 4, 5, 6, 9,单调递增,偶数位置上的值是 2, 3, 7,8,也是单调递增。

请问,1 到 n 的排列中有多少个半递增序列?

【输⼊格式】

输⼊⼀⾏包含⼀个正整数 n。

【输出格式】

输出⼀⾏包含⼀个整数,表⽰答案,答案可能很⼤,请输出答案除以 1000000007 的余数。

【样例输⼊】

5

【样例输出】

10

【样例说明】

有以下半递增序列:

(1, 2, 3, 4, 5)
(1, 2, 3, 5, 4)
(1, 2, 4, 3, 5)
(1, 3, 2, 4, 5)
(1, 3, 2, 5, 4)
(1, 4, 2, 5, 3)
(2, 1, 3, 4, 5)
(2, 1, 3, 5, 4)
(2, 1, 4, 3, 5)
(3, 1, 4, 2, 5)

【评测⽤例规模与约定】

对于 50% 的评测⽤例,2 <= n <= 20。
对于所有评测⽤例,2 <= n <= 1000。

⭐️题解:组合数

从 n 个数中选出 (n / 2) 个数,使它们升序排列,放在偶数位置上,剩下的数升序排列放在奇数位置上

所以答案是 C(n, n / 2)

#include <iostream>
using namespace std;

const int MOD = 1E9 + 7;

int getc(int a, int b) {
    b = min(b, a - b);
    long long ans = 1;
    for (int i = 0; i < b; i++) {
        ans = (ans * (a - i) / (i + 1)) % MOD;
    }
    return ans;
}

int main() {
    int n;
    cin >> n;
    cout << getc(n, n / 2) << endl;
    return 0;
}

🚩试题R

⭐️问题描述

⼩蓝住在 LQ 城,今天他要去⼩乔家玩。

LQ 城可以看成是⼀个 n ⾏ m 列的⼀个⽅格图。

⼩蓝家住在第 1 ⾏第 1 列,⼩乔家住在第 n ⾏第 m 列。

⼩蓝可以在⽅格图内⾛,他不愿意⾛到⽅格图外。

城市中有的地⽅是风景优美的公园,有的地⽅是熙熙攘攘的街道。⼩蓝很喜欢公园,不喜欢街道。他把⽅格图中的每⼀格都标注了⼀个属性,或者是喜欢的公园,标为1,或者是不喜欢的街道标为2。⼩蓝和⼩乔住的地⽅都标为了1。

⼩蓝每次只能从⼀个⽅格⾛到同⼀⾏或同⼀列的相邻⽅格。他想找到⼀条路径,使得不连续⾛两次标为 2 的街道,请问在此前提下他最少要经过⼏次街道?

【输⼊格式】

输⼊的第⼀⾏包含两个整数 n, m,⽤⼀个空格分隔。接下来 n ⾏,每⾏⼀个长度为 m 第数字串,表⽰城市的标注。

【输出格式】

输出⼀⾏包含⼀个整数,表⽰答案。如果没有满⾜条件的⽅案,输出 -1。

【样例输⼊】

3 4
1121
1211
2211

【样例输出】

2

【样例输⼊】

3 4
1122
1221
2211

【样例输出】

-1

【样例输⼊】

5 6
112121
122221
221212
211122
111121

【样例输出】

5

【 评测⽤例规模与约定】

对于 50% 的评测⽤例,2 <= n, m <= 20。
对于所有评测⽤例,2 <= n, m <= 300。

⭐️题解:记忆化搜索
 

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

const int MAXN = 305;
int m, n;
string s[MAXN];
int vis[MAXN][MAXN];
vector<vector<int> > mp;
int a[][2] = {{1, 0}, {-1, 0}, {0, 1}, {0, -1}};

int dfs(int x, int y) {
    if (x == m - 1 && y == n - 1) return 0;
    if (mp[x][y] != -2) return mp[x][y];
    int ans = -1, tmp;
    for (int i = 0; i < 4; i++) {
        int xx = x + a[i][0];
        int yy = y + a[i][1];
        if (xx >= 0 && xx < m && yy >= 0 && yy < n && vis[xx][yy] == 0) {
            if (s[x][y] == '2' && s[xx][yy] == '2') continue;
            vis[xx][yy] = 1;
            tmp = dfs(xx, yy);
            vis[xx][yy] = 0;
            if (ans == -1) ans = tmp;
            else if(tmp != -1) ans = min(ans, tmp);
        }
    }
    if (ans != -1 && s[x][y] == '2') ans++;
    mp[x][y] = ans;
    return ans;
}

int main() {
    cin >> m >> n;
    for (int i = 0; i < m; i++) {
        cin >> s[i];
    }
    mp.resize(m, vector<int>(n, -2));
    vis[0][0] = 1;
    cout << dfs(0, 0) << endl;;
    return 0;
}

举报

相关推荐

0 条评论