蓝桥杯复习清单真题(C++版本)

蓝桥杯复习清单

枚举

日期统计

问题描述

小蓝现在有一个长度为 100 的数组,数组中的每个元素的值都在 0 到 9 的范围之内。数组中的元素从左至右如下所示:

复制代码
5 6 8 6 9 1 6 1 2 4 9 1 9 8 2 3 6 4 7 7 5 9 5 0 3 8 7 5 8 1
5 8 6 1 8 3 0 3 7 9 2
7 0 5 8 8 5 7 0 9 9 1 9 4 4 6 8 6 3 3 8 5 1 6 3 4 6 7 0 7 8
2 7 6 8 9 5 6 5 6 1 4 0 1
0 0 9 4 8 0 9 1 2 8 5 0 2 5 3 3

现在他想要从这个数组中寻找一些满足以下条件的子序列:

  1. 子序列的长度为 8;
  2. 这个子序列可以按照下标顺序组成一个 yyyymmdd 格式的日期,并且要求这个日期是 2023 年中的某一天的日期,例如 20230902, 20231223。yyyy 表示年份,mm 表示月份,dd 表示天数,当月份或者天数的长度只有一位时需要一个前导零补充。

请你帮小蓝计算下按上述条件一共能找到多少个不同的 2023 年的日期。对于相同的日期你只需要统计一次即可。

解法:

由题意可知,要找⽇期全是 2023 年份的,只有⽉和⽇会变化。因此,可以先枚举 2023 年的每⼀个⽉和⽇,然后判断序列中是否存在这个⽇期。

枚举 2023 年的每⼀个⽉和⽇的时间开销为 365,判断数组中是否存在这个⽇期的时间开销为 100,效率更⾼。

代码如下:

C++ 复制代码
#include <iostream>
using namespace std;
int n = 100;// 数组长度固定为 100
int a[110];// 存储输入的数字序列,下标从1开始
int days[] = { 0,31,28,31,30,31,30,31,31,30,31,30,31 };//days[0]占位不用,days[1]是 1 月的天数,依此类推
int main()
{
    for (int i = 1; i <= n; i++) cin >> a[i];
    int cnt = 0;
    for (int m = 1; m <= 12; m++)//枚举月份
        for (int d = 1; d <= days[m]; d++)//枚举天数
        {
            int t[] = { 2,0,2,3,m / 10,m % 10,d / 10,d % 10 };
            for (int i = 0, j = 1; j <= n; j++)
            {
                if (a[j] == t[i])// // 当前数字匹配目标序列
                {
                    i++;//匹配下一个数
                    if (i == 8)// 8 个数字全部匹配成功!
                    {
                        cnt++;// 计数器+1
                        break;// 跳出循环,处理下一个日期
                    }
                }
            }
        }
    cout << cnt << endl;
    return 0;
}

二分查找

冶炼金属

【题目描述】

小蓝有一个神奇的炉子用于将普通金属 O 冶炼成为一种特殊金属 X。这个炉子有一个称作转换率的属性 V,V 是一个正整数,这意味着消耗 V 个普通金属 O 恰好可以冶炼出一个特殊金属 X ,当普通金属 O 的数目不足 V 时,无法继续冶炼。

现在给出了 N 条冶炼记录,每条记录中包含两个整数 A 和 B,这表示本次投入了 A 个普通金属 O,最终冶炼出了 B 个特殊金属 X。每条记录都是独立的,这意味着上一次没消耗完的普通金属 O 不会累加到下一次的冶炼当中。

根据这 N 条冶炼记录,请你推测出转换率 V 的最小值和最大值分别可能是多少,题目保证评测数据不存在无解的情况。

【输入描述】

第一行一个整数 N,表示冶炼记录的数目。

接下来输入 N 行,每行两个整数 A、B,含义如题目所述。

【输出描述】

输出两个整数,分别表示 V 可能的最小值和最大值,中间用空格分开。

【示例一】

输入:

3

75 3

53 2

59 2

输出:

20 25

代码如下:

C++ 复制代码
#include <iostream>
using namespace std;
const int N = 1e4 + 10;
int n, a[N], b[N];
bool check1(int v)
{
	for (int i = 1; i <= n; i++)
		if (a[i] / v > b[i])
			return false;
	return true;
}
bool check2(int v)
{
	for (int i = 1; i <= n; i++)
		if (a[i] / v < b[i])
			return false;
	return true;
}
int main()
{
	cin >> n;
	for (int i = 1; i <= n; i++) cin >> a[i] >> b[i];
	//二分最小值
	int l = 1, r = 1e9;
	while (l < r)
	{
		int mid = (l + r) / 2;
		if (check1(mid)) r = mid;
		else l = mid + 1;
	}
	cout << l << " ";
	//二分最大值
	l = 1, r = 1e9;
	while (l < r)
	{
		int mid = (l + r + 1) / 2;//mid = (l + r + 1) / 2是向上取整,避免在 l=r-1 时死循环。
		if (check2(mid)) l = mid;
		else r = mid - 1;
	}
	cout << l << endl;
	return 0;
}

DFS

飞机降落

【题目描述】

N架飞机准备降落到某个只有一条跑道的机场。其中第 i架飞机在 Ti时刻到达机场上空,到达时它的剩余油料还可以继续盘旋 Di个单位时间,即它最早可以于 Ti时刻开始降落,最晚可以于 Ti+Di时刻开始降落。降落过程需要 Li个单位时间。

一架飞机降落完毕时,另一架飞机可以立即在同一时刻开始降落,但是不能在前一架飞机完成降落前开始降落。

请你判断 N架飞机是否可以全部安全降落。

【输入描述】

输入包含多组数据。

第一行包含一个整数 T,代表测试数据的组数。

对于每组数据,第一行包含一个整数 N。

以下 N行,每行包含三个整数:Ti,Di和 Li。

【输出描述】

对于每组数据,输出 YES或者 NO,代表是否可以全部安全降落。

【示例一】

输入:

2

3

0 100 10

10 10 10

0 2 20

3

0 10 20

10 10 20

20 10 20

输出:

YES

NO

代码如下:

C++ 复制代码
#include <iostream>
#include <cstring>
using namespace std;
const int N = 15;
int n, t[N], d[N], l[N];
bool st[N];
bool dfs(int pos, int end)
{
	//pos:当前要安排第几架飞机(已安排了 pos-1 架)
	//end:当前跑道的可用时间(上一架飞机降落结束的时间)
	if (pos > n) return true;// 所有飞机都安排好了
	for (int i = 1; i <= n; i++)
	{
		if (st[i]) continue;
		//如果最晚开始时间< 跑道可用时间,那么飞机无法降落(油料不够等到跑道空闲)
		if (t[i] + d[i] < end) continue;
		st[i] = true;
		int newend = max(t[i], end) + l[i];
		if (dfs(pos + 1, newend)) return true;
		st[i] = false;
	}
	return false;
}
int main()
{
	int T;
	cin >> T;
	while (T--)
	{
		memset(st, 0, sizeof st);
		cin >> n;
		for (int i = 1; i <= n; i++) cin >> t[i] >> d[i] >> l[i];
		if (dfs(1, 0)) cout << "YES" << endl;
		else cout << "NO" << endl;
	}
	return 0;
}

线性DP-最长递增子序列

接龙数列

问题描述

对于一个长度为 K 的整数数列:A₁, A₂, ..., Aₖ,我们称之为接龙数列当且仅当 Aᵢ 的首位数字恰好等于 Aᵢ₋₁ 的末位数字(2 ≤ i ≤ K)。例如 12, 23, 35, 56, 61, 11 是接龙数列;12, 23, 34, 56 不是接龙数列,因为 56 的首位数字不等于 34 的末位数字。所有长度为 1 的整数数列都是接龙数列。

现在给定一个长度为 N 的数列 A₁, A₂, ..., Aₙ,请你计算最少从中删除多少个数,可以使剩下的序列是接龙序列?

输入格式

第一行包含一个整数 N。

第二行包含 N 个整数 A₁, A₂, ..., Aₙ。

输出格式

一个整数代表答案。

样例输入

5

11 121 22 12 2023

样例输出

1

样例说明

删除 22,剩余 11, 121, 12, 2023 是接龙数列。

代码如下:

C++ 复制代码
#include <iostream>
#include <string>
using namespace std;
const int N = 1e5 + 10;
int n;
int len[10];  // len[i] 表示:以数字 i 结尾的接龙序列的最大长度

int main() {
    cin >> n;
    int ret = 0;  // 记录全局最长的接龙序列长度

    for (int i = 1; i <= n; i++) {
        string s;
        cin >> s;
        int l = s[0] - '0';       // 首位数字
        int r = s.back() - '0';   // 末位数字

        int t = len[l] + 1;        // 当前数字可以接在以 l 结尾的序列后面
        len[r] = max(len[r], t);  // 更新以 r 结尾的序列最大长度
        ret = max(ret, t);        // 更新全局最大值
    }

    cout << n - ret << endl;  // 最少删除数 = 总数 - 最长接龙序列长度
    return 0;
}

FloodFill问题

岛屿个数

问题描述

小蓝得到了一副大小为 M × N 的格子地图,可以将其视作一个只包含字符 '0'(代表海水)和 '1'(代表陆地)的二维数组,地图之外可以视作全部是海水,每个岛屿由在上/下/左/右四个方向上相邻的 '1' 相连接而形成。

在岛屿 A 所占据的格子中,如果可以从中选出 k 个不同的格子,使得他们的坐标能够组成一个这样的排列:(x₀, y₀), (x₁, y₁), ..., (xₖ₋₁, yₖ₋₁),其中 (xᵢ₊₁ mod k, yᵢ₊₁ mod k) 是由 (xᵢ, yᵢ) 通过上/下/左/右移动一次得来的(0 ≤ i ≤ k - 1),此时这 k 个格子就构成了一个"环"。如果另一个岛屿 B 所占据的格子全部位于这个"环"内部,此时我们将岛屿 B 视作是岛屿 A 的子岛屿。若 B 是 A 的子岛屿,C 又是 B 的子岛屿,那 C 也是 A 的子岛屿。

请问这个地图上共有多少个岛屿?在进行统计时不需要统计子岛屿的数目。

输入格式

第一行一个整数 T,表示有 T 组测试数据。

接下来输入 T 组数据。对于每组数据,第一行包含两个用空格分隔的整数 M、N 表示地图大小;接下来输入 M 行,每行包含 N 个字符,字符只可能是 '0' 或 '1'。

输出格式

对于每组数据,输出一行,包含一个整数表示答案。

样例输入

2

5 5

01111

11001

10101

10001

11111

5 6

111111

100001

010101

100001

111111

样例输出

1

3

代码如下:

C++ 复制代码
#include <iostream>
#include <cstring>
using namespace std;
const int N = 55;
int n, m, cnt;
int g[N][N];
bool st[N][N];

// 4方向:上下左右(用于岛屿标记)
int dx4[] = {-1, 1, 0, 0};
int dy4[] = {0, 0, -1, 1};

// 8方向:包括对角线(用于海水淹没)
int dx8[] = {-1, 1, 0, 0, -1, -1, 1, 1};
int dy8[] = {0, 0, -1, 1, -1, 1, -1, 1};

// 标记整个岛屿(4连通)
void dfs_mark_island(int a, int b) {
    st[a][b] = true;
    for (int i = 0; i < 4; i++) {
        int x = a + dx4[i], y = b + dy4[i];
        if (x < 1 || x > n || y < 1 || y > m || st[x][y]) continue;
        if (g[x][y]) dfs_mark_island(x, y);
    }
}

// 海水淹没(8连通)
void dfs_flood(int a, int b) {
    st[a][b] = true;
    for (int i = 0; i < 8; i++) {
        int x = a + dx8[i], y = b + dy8[i];
        if (x < 0 || x > n + 1 || y < 0 || y > m + 1 || st[x][y]) continue;
        
        if (g[x][y]) {  // 遇到陆地
            cnt++;  // 这是一个新岛屿
            dfs_mark_island(x, y);  // 标记整个岛屿,防止重复计数
        } else {
            dfs_flood(x, y);  // 继续淹没海水
        }
    }
}

int main() {
    int T; 
    cin >> T;
    while (T--) {
        // 初始化
        memset(g, 0, sizeof g);
        memset(st, 0, sizeof st);
        cnt = 0;
        
        cin >> n >> m;
        
        // 读入地图
        for (int i = 1; i <= n; i++) {
            for (int j = 1; j <= m; j++) {
                char ch;
                cin >> ch;
                g[i][j] = ch - '0';
            }
        }
        
        // 从地图外(0,0)开始海水淹没
        // 注意:g[0][0]一定是0(海水),因为地图外都是海水
        dfs_flood(0, 0);
        
        cout << cnt << endl;
    }
    return 0;
}

模拟

产值调整

问题描述

偏远的小镇上,三兄弟共同经营着一家小型矿业公司"兄弟矿业"。公司旗下有三座矿山:金矿、银矿和铜矿,它们的初始产值分别用非负整数 A、B和 C表示。这些矿山的产出是小镇经济的核心,支撑着三兄弟和许多矿工家庭的生计。

然而,各矿山的产值波动剧烈,有时金矿收益高而银矿、铜矿低迷,有时则相反。这种不稳定性让公司收入难以预测,也常引发兄弟间的争执。为了稳定经营,三兄弟设计了一个公平的产值调整策略,每年执行一次,每次调整时,将根据当前的产值 A、B、C,计算新产值:

  • 金矿新产值 A′=⌊2B+C⌋;
  • 银矿新产值 B′=⌊2A+C⌋;
  • 铜矿新产值 C′=⌊2A+B⌋。

其中,⌊⌋表示向下取整。例如,⌊3.7⌋=3,⌊5.2⌋=5。

计算出 A′、B′、C′后,同时更新:A变为 A′,B变为 B′,C变为 C′,作为下一年调整的基础。

三兄弟认为这个方法能平衡产值波动,于是计划连续执行 K次调整。现在,请你帮他们计算,经过 K次调整后,金矿、银矿和铜矿的产值分别是多少。

输入格式

输入的第一行包含一个整数 T,表示测试用例的数量。

接下来的 T行,每行包含四个整数 A,B,C,K,分别表示金矿、银矿和铜矿的初始产值,以及需要执行的调整次数。

输出格式

对于每个测试用例,输出一行,包含三个整数,表示经过 K次调整后金矿、银矿和铜矿的产值,用空格分隔。

样例输入

2

10 20 30 1

5 5 5 3

样例输出

25 20 15

5 5 5

代码如下:

C++ 复制代码
include <iostream>
using namespace std;
int main()
{
  int T; cin >> T;
    while(T--)
    {
        int a, b, c, k; cin >> a >> b >> c >> k;
        while(k--)
        {
            int x = (b + c) / 2, y = (a + c) / 2, z = (a + b) / 2;
            a = x, b = y, c = z;
            if(a == b && a == c) break;
        }

        cout << a << " " << b << " " << c << endl;
    }
}

01BFS

水质检测

问题描述

小明需要在一条 2×n的河床上铺设水质检测器。在他铺设之前,河床上已经存在一些检测器。如果两个检测器上下或左右相邻,那么这两个检测器就是互相连通的。

连通具有传递性,即如果 A和 B连通,B和 C连通,那么 A和 C也连通。现在他需要在河床上增加铺设一些检测器,使得所有检测器都互相连通。他想知道最少需要增加铺设多少个检测器?

输入格式

输入共两行,表示一个 2×n的河床。

每行一个长度为 n的字符串,仅包含 #.,其中 #表示已经存在的检测器,.表示空白。

输出格式

输出共 1 行,一个整数,表示最少需要增加的检测器数量。

样例输入

复制代码
.##....#
.#..#...

样例输出

复制代码
5

样例说明

其中一种方案:###....#.#######

增加了 5 个检测器。

代码如下:

C++ 复制代码
#include <iostream>
#include <queue>
#include <cstring>
using namespace std;

const int N = 1e6 + 10;
int n;
char g[2][N];
int dist[2][N];
bool st[2][N];
int dx[] = {0, 0, 1, -1};
int dy[] = {1, -1, 0, 0};

int bfs(int i, int j) {
    int ret = 0;
    memset(dist, 0x3f, sizeof dist);
    memset(st, 0, sizeof st);  // 每次BFS需要重置st数组!
    
    deque<pair<int, int>> q;
    q.push_back({i, j});
    dist[i][j] = 0;
    
    while(q.size()) {
        auto t = q.front(); 
        q.pop_front();
        int a = t.first, b = t.second;
        
        if(st[a][b]) continue;
        st[a][b] = true;
        
        if(g[a][b] == '#') 
            ret = max(ret, dist[a][b]);
        
        for(int k = 0; k < 4; k++) {
            int x = a + dx[k], y = b + dy[k];
            if(x < 0 || x >= 2 || y < 0 || y >= n) continue;
            
            int w = (g[x][y] == '#') ? 0 : 1;
            
            if(dist[a][b] + w < dist[x][y]) {
                dist[x][y] = dist[a][b] + w;
                if(w == 0) 
                    q.push_front({x, y});
                else 
                    q.push_back({x, y});
            }
        }
    }
    return ret;
}

int main() {
    cin >> g[0] >> g[1];
    n = strlen(g[0]);
    
    // 找到第一个已有的检测器作为起点
    for(int j = 0; j < n; j++) {
        if(g[0][j] == '#') {
            cout << bfs(0, j) << endl;
            return 0;
        }
        if(g[1][j] == '#') {
            cout << bfs(1, j) << endl;
            return 0;
        }
    }
    
    // 如果没有检测器
    cout << 0 << endl;
    return 0;
}
相关推荐
tankeven2 小时前
HJ163 时津风的资源收集
c++·算法
森G2 小时前
40、对话框---------事件系统
c++·qt
Boop_wu2 小时前
[Java 算法] 动态规划(4)
数据结构·算法·leetcode
旖-旎2 小时前
分治(计算右侧小于当前元素的个数)(7)
c++·学习·算法·leetcode·排序算法·归并排序
迷海2 小时前
C++内存对齐
开发语言·c++
cxr8282 小时前
细胞球运动追踪的卡尔曼滤波与力场插值算法 —— 活体内微米级颗粒实时定位与轨迹预测系统
算法
炘爚2 小时前
C++(流类:istream /ostream/istringstream /ostringstream)
开发语言·c++·算法
yoyobravery2 小时前
蓝桥杯第十二届4t满分
蓝桥杯
!停2 小时前
C++入门—内存管理
java·jvm·c++