蓝桥杯青少C++省赛备赛指南和真题详解

蓝桥杯青少C++省赛备赛指南和真题详解_第1张图片
蓝桥杯大赛是工业和信息化部人才交流中心举办的全国性专业信息技术赛事,已经成功举办了13届,历时14年。蓝桥杯大赛首席专家倪光南院士说:“蓝桥杯以考促学,塑造了领跑全国的人才培养选拔模式,并获得了行业的深度认可。”

随着蓝桥杯加入国家白名单赛事,含金量更是得到了进一步的提升。
蓝桥杯青少C++省赛备赛指南和真题详解_第2张图片
蓝桥杯有很多条赛道,其中和我们关联比较紧密的C++赛道 省赛时间为:

C++ 初级/中级/高级 2023年5月14日 10:00—11:30。

在接下来的备赛期间,建议同学们做好以下几点:
1.夯实基础
牢记常用模板,大量练习总结,提升代码熟练度。大量的练习和总结是提高编程能力的关键。可以通过参加线上编程竞赛、刷题网站等方式进行练习。

2.抓住重点
除了基础阶段我们常用的基础算法和模板外,要重点掌握 线性dp、0/1背包、还有基本的搜索算法,如果还没掌握相关知识的同学,建议自己寻找相关的书籍和视频资料,提前学习,这些算法在蓝桥杯真题中出现频率很高,想拿到好成绩是必须要掌握的。

3.参加模拟赛
通过参加模拟赛,可以更好地了解自己在比赛环境中的表现。在模拟赛中,可以发现自己的不足之处,并在之后的训练中加以改进。

4.把握真题
刷真题在备赛过程中具有重要意义,它可以帮助你了解题型和难度,提高解题能力和自信心,检验学习效果,培养时间管理能力,以及学习优秀选手的解题思路。通过刷真题,你可以更好地针对性地进行训练,从而在比赛中取得更好的成绩。

下面是近两年的蓝桥青少C++组的题目和解析

蓝桥青少 2022 C++中级 省赛真题(十三届)

选择题1: (20分)

以下对main函数描述正确的是( )。

A. main函数必须写在所有函数的前面

B. main函数必须写在所有函数的后面

C. main 函数可以写在任何位置,但不能放到其他函数里

D. main 函数必须写在固定位置

**正确答案为 C. **

解释:main 函数可以写在程序的任何位置,只要不把它放到其他函数里。但是一般情况下,我们都把 main 函数写在程序的最前面或最后面,以便于程序的阅读和理解。

选择题2: (20分)

已知char a; float b; double c; 执行语句c = a + b + c;后变量c的类型是( )。

A. char B. float C. double D. int

正确答案为 C. double.

解释:变量的数据类型不会因为赋值而改变

选择题3: (20分)

二逬制数1101111转换为十六进制是( )。

A. 157 B. 111 C. 6f D. 3f

正确答案为 C. 6f.

解释:将二进制数 1101111 按四位一组分组为 0110 和 1111,然后将每一组转换为十六进制数,分别是 6 和 f,因此 1101111 的十六进制表示为 6f。

选择題4: (20分)

下列函数中哪一个不能重载( )。

A.构造函数 B.析构函数 C. 成员函数 D. 非成员函数

正确答案为 B. 析构函数.

解释:析构函数不能被重载,因为它的名称是固定的,并且它的参数列表为空。构造函数可以被重载,因为它的名称与类名相同,但参数列表可以不同。成员函数和非成员函数也可以被重载,只要它们的名称相同但参数列表不同。

选择题5: (20分)

下列指针的用法中哪—不正确( )。

A. int i; int *p = &i;
B. Int i; int *p; i = *p;
C. int *p; p = 0;
D. int i = 5; int *p; p = &i;

**正确答案为 B. Int i; int p; i = p;

**解释:在执行 i = p; 时,指针 p 没有指向任何变量,因此这条语句会导致未定义的行为。正确的做法应该是先让指针 p 指向一个已经定义的变量,然后再使用 p 访问该变量的值。因此,选项 B 中的语句是不正确的。

第一题:编程实现:比较大小(30分)

题目描述:

给定两个正整数N和M(0 < N < 200, 0 < M < 200, N ≠ M),比较两个正整数的大小,然后将较大的一个正整数输出。
例如:N=145,M=100,比较后145大于100,故输出145。

输入描述:

​ 输入两个正整数N和M(0

输出描述:

​ 输出一个正整数,表示N和M中较大的一个正整数

样例输入:

145 100

样例输出:

145

#include 
using namespace std;
int main() {
    int n, m;  
    cin >> n >> m;  
    if (n > m) { 
        cout << n;  
    } else {
        cout << m; 
    }
    return 0;  
}

第二题:编程实现:分解整数(40分)

题目描述:

给定一个正整数 N ,然后将 N 分解成 3 个正整数之和。计算出共有多少种符合要求的分解方法。

要求:
1)分解的3个正整数各不相同;
2)分解的3个正整数中都不含数字3和7.
如:N为8,可分解为(1,1,6)、(1,2,5)、(1,3,4)、(2,2,4)、(2,3,3),其中满足要求的分解方法有1种,为(1,2,5)。

输入描述:

输入一个正整数N (5 < N < 501),表示需要分解的正整数

输出描述:

输出一个整数,表示共有多少种符合要求的分解方法

样例输入:

8

样例输出:

1

提示信息:

因数:因数是指整数a除以整数b(b≠0)的商正好是整数而设没有余数,我们就说b是a的因数。
公因数:给定若干个整数,如果有一个(些)数是它们共同的因数,那么这个(些)数就叫做它们的公因数。
互质数:公因数只有1的两个非零自然数,叫做互质数:例如:2和3,公因数只有1,为互质数。

/*
	解题思路:
	 
	 首先,使用 cin 读入正整数 N。
	 然后,使用两个嵌套的 for 循环枚举三个正整数的值,分别为 i,j 和 k。
	 
	 为了保证数字不相同,我们让i,j,k递增。 
	 其中,i 的范围为 [1, N/3],j 的范围为 [i+1, N/3*2-i],k 的值等于 N - i - j。
	 
	 在枚举过程中,使用 if 语句判断 i,j,k 是否满足要求。
	 如果满足要求,则将符合要求的分解方法数量 count 增加 1。
	 最后,输出符合要求的分解方法数量 count,返回 0,表示程序正常结束。

*/
#include 
using namespace std;

 //检查 数字是否包含 7 或3 
bool check(int n) { 
	while(n){
		if(n % 10 == 3 || n % 10 ==7)
			return 0;
		n/=10;
	}
	return 1; 
	
} 
int main() {
    int N, count = 0;  // 定义整数变量N和count,count表示符合要求的分解方法数量
    cin >> N;  // 读入N的值
    for (int i = 1; i <= N / 3; i++) {  // 枚举第一个数i,i的范围为[1,N/3]
        for (int j = i + 1; j <= N / 3 * 2 - i; j++) {  // 枚举第二个数j,j的范围为[i+1,N/3*2-i]
            int k = N - i - j;  // 计算第三个数k
            if (check(i) && check(j) && check(k)) {   // 判断i,j,k是否符合要求
              
                count++;  // 如果符合要求,则增加count的值

            }
        }
    }
    cout << count;  // 输出符合要求的分解方法数量
    return 0;  
}

第三题: 编程实现:买糖果 (50分)

题目描述:

某商店将一种糖果按照数量打包成N和M两种规格来售卖(N和M为互质数,且N和M有无数包)。这样的售卖方式会限制一些数量的糖果不能买到。给出N和M的值,请你计算出最多不能买到的糖果数量。

例如:
当N = 3, M = 5, 3 和 5 为互质数,不能买到的糖果数量有1, 2, 4, 7,最多不能买到的糖果数量就是7, 7之后的任何数量的糖果都是可以通过组合购买到的。

输入描述:

输入两个正整数N,M (2 < N < M < 100,N和M为互质数),表示两种规格的糖果数量,正整数之间一个空格隔开!

输出描述:

输出一个整数,表示最多不能买到的糖果数量:

样例输入:

3 5

样例输出:

7

#include 
using namespace std;
int a, b;
int main() {
	cin >> a >> b;

	for(int k = a* b; k >= 1; k--){
		// a * i + b * j == k
		int flag = 0;
		for (int i = 0; i < 100; i++) {
			int j = k - a * i;
			if (j >= 0 && j % b == 0) {
				flag = 1;
				break;
			}	
		}
		if (flag == 0) {
			cout << k << endl;
			break;
		}
	}
	return 0;
}
如何证明: 给定两个互质的正整数a,b,a*b以上的数字一定可以 由若干个a和若干个b组合出来.

要证明这个结论,我们需要利用互质数的性质。
由于a和b是互质的,所以它们的最大公约数gcd(a, b) = 1。因此,由裴蜀定理可得存在整数x和y,使得ax + by = 1。我们可以对这个等式进行变形,得到:
ax + by = 1 ax(a(b-1) + y) + b(-b(x-1)) = ab - 1

因为x、y、a、b都是整数,所以ax(a(b-1) + y)和b(-b(x-1))也是整数。因此,我们可以把右边的ab - 1表示成若干个a和b的和,即:
ab - 1 = ax(a(b-1) + y) + b(-b(x-1)) + kab

其中k为一个整数。我们可以进一步变形,得到:
ab = a(bx + k) + b(ay - b(x-1))

因此,我们证明了对于任意大于等于ab的整数,都可以表示成若干个a和b的和的形式。具体地,设这个整数为c,则我们可以先找到一个最大的k,使得c - ak大于等于b,然后我们可以用若干个a表示出c - ak,再加上k个b,即可表示出c。由于c大于等于ab,所以这个表示一定是合法的。

因此,我们证明了结论:给定两个互质的正整数a和b,a*b以上的任意整数都可以表示成若干个a和b的和。

第四题:编程实现:最大值(60分)

题目描述:

手工课上老师拿出N张长方形彩纸,且每张彩纸上都画着W * H的网格(网格铺满整张彩纸)。现在老师将N张彩纸裁剪出K张大小相同的正方形,并且要使裁剪出的正方形的边长最大(裁剪的正方形边长必须为整数)。

例如:N=2, 有2张彩纸,第一张彩纸W = 4, H = 3:第二张彩纸W = 5,H = 4; K = 6,裁剪的6个正方形边长最大是2。

蓝桥杯青少C++省赛备赛指南和真题详解_第3张图片

当给出N张长方形彩纸W和H,及K的值,请青计算出将N张彩纸裁剪出K张大小相同的正方形,正方形的边长最大是多少(裁剪的正方形边长必须为整数)。

输入描述:

第一行输入两个正整数N,K(1 第二行开始,输入N行,每行输入两个正整数Wi,Hi (1 < Wi < 1000,1 < Hi < 1000,且 Wi≠Hi,,Wi表示彩纸的长度,Hi表示彩纸的宽度,两个正整数之间一个空格隔开.

输出描述:

输出一个正整数,表示将N张彩纸裁剪出K张大小相同的正方形的边长最大是多少(裁剪的正方形边长必须为整数),如果不能裁剪出K张正方形就输出“-1”

样例输入:

2 6
4 3
5 4

样例输出:

2

/*
题目要求将N张彩纸裁剪出K张大小相同的正方形,并且要使裁剪出的正方形的边长最大,裁剪的正方形边长必须为整数。

因为要裁剪的正方形大小是一样的,所以可以把所有彩纸的网格拼接在一起,然后对整个网格进行切割,最终得到的正方形大小就是所有裁剪出的正方形的大小。而要求裁剪出的正方形的边长最大,也就是要求最终得到的正方形大小最大。

因此,我们可以采用二分答案的思想。设定一个边长mid,然后对整个网格进行切割,看能否得到K个大小为mid的正方形。如果可以,那么继续增大mid;如果不行,那么缩小mid。

具体实现时,可以用一个check函数来检查在给定的边长下,是否能够得到K个正方形。check函数中,可以用贪心的思想,从左上角开始切割,每次切割出一个正方形,直到切割出K个正方形或者无法再切割为止。如果能够切割出K个正方形,说明当前的mid太小,需要增大;否则,说明当前的mid太大,需要缩小。

时间复杂度:O(NWlog(S)),其中W为彩纸的最大边长,S为所有彩纸网格的总面积。

*/
#include 
#include 
using namespace std;

const int MAX_N = 100;  // 彩纸数量的最大值
const int MAX_W = 1000;  // 彩纸长度和宽度的最大值
int n, k;  // n为彩纸数量,k为需要裁剪的正方形数量
int paper_list[MAX_N][2];  // 存储所有彩纸的宽度和长度

// 定义check函数,检查在给定的边长mid下,是否能够得到K个正方形
bool check(int mid) {
    int cnt = 0;  // 统计切割出的正方形数量
    for (int i = 0; i < n; i++) {
        int w = paper_list[i][0], h = paper_list[i][1];
        // 以左上角为起点,依次切割大小为mid的正方形
        for (int x = 0; x <= w - mid; x += mid) {
            for (int y = 0; y <= h - mid; y += mid) {
                cnt++;  // 切割出一个正方形
                if (cnt >= k) return true;  // 切割出了足够数量的正方形
            }
        }
    }
    return false;  // 无法切割出足够数量的正方形
}

int main() {
    cin >> n >> k;
    for (int i = 0; i < n; i++) {
        int w, h;
        cin >> w >> h;
        paper_list[i][0] = w;
        paper_list[i][1] = h;
    }
    int left = 1, right = MAX_W;  // 左右边界,题目中已给出w和h的最大值为1000
    int ans = -1;  // 初始化答案为-1
    while (left <= right) {
        int mid = (left + right) / 2;
        if (check(mid)) {  // 可以切割出K个正方形
            ans = mid;  // 更新答案
            left = mid + 1;  // 向右二分
        } else {  // 无法切割出K个正方形
            right = mid - 1;  // 向左二分
        }
    }
    cout << ans << endl;  // 输出答案
    return 0;
}


第五题:编程实现:农作物(70分)

题目描述:

有一块农田被划分为N * M块,农作物和杂草分布生长在农田中,其中农作物使用大写字母“R”表示,杂草使用大写字母 “X” 表示。请计算出农田中有几块独立的农作物区域(独立的农作物区域指该区域上下左右都被杂草围住,且 N * M 以外的区域都是杂草)。

例如:N = 4, M = 4, 4 * 4的农田中农作物和杂草分布如下图:

蓝桥杯青少C++省赛备赛指南和真题详解_第4张图片

这块 4*4 的农田中有3块独立的农作物区域(红色的3部分)。

输入描述:

第一行输入两个整数N和M(1 ≤ N ≤ 100, 1 ≤ M ≤ 100),N表示农田的行数,M表示农田的列数,且两个正整数之间一个空格隔开
接下来的N行每行包括M个字符(字符只能为R或),R表示农作物,X表示杂草,字符之间一个空格隔开

输出描述:

输出一个整数,表示NM的农田中有几块独立的农作物区域

样例输入:

4 4
RRRX
RXRX
XXXR
RXXX

样例输出:

3

/*

	下面给出DFS的实现代码,其中visited数组用于记录已搜索过的位置,
	对于每个新的'R',调用DFS函数进行搜索,并将搜索到的'R'标记为已搜索。
	
	这里使用了两个二维数组 field 和 visited 分别存储农田的状态和是否被访问的信息。
	dfs 函数用于深度优先搜索,遍历与当前位置相连的所有农作物区域,并将其全部标记为已访问,
	以确保每个农作物区域只被统计一次。最后,遍历整个农田,如果遇到未被访问过的农作物,
	就进行一次深度优先搜索,并将计数器加一。
*/

#include 
using namespace std;

const int MAXN = 105;
char field[MAXN][MAXN];
bool visited[MAXN][MAXN];

int N, M;
int dx[] = {-1, 0, 1, 0};  // 上右下左四个方向
int dy[] = {0, 1, 0, -1};

void dfs(int x, int y) {
    visited[x][y] = true;
    for (int i = 0; i < 4; i++) {
        int nx = x + dx[i];
        int ny = y + dy[i];
        if (nx < 0 || nx >= N || ny < 0 || ny >= M) continue;  // 超出边界
        if (field[nx][ny] == 'X') continue;  // 是杂草
        if (visited[nx][ny]) continue;  // 已经访问过
        dfs(nx, ny);
    }
}

int main() {
    cin >> N >> M;
    for (int i = 0; i < N; i++) {
        for (int j = 0; j < M; j++) {
            cin >> field[i][j];
        }
    }

    int cnt = 0;
    for (int i = 0; i < N; i++) {
        for (int j = 0; j < M; j++) {
            if (field[i][j] == 'R' && !visited[i][j]) {  // 是农作物且未被访问过
                dfs(i, j);
                cnt++;
            }
        }
    }

    cout << cnt << endl;
    return 0;
}

第六题:编程实现:面积(80分)

题目描述:

小蓝要给墙面上的N个矩形区域粉刷涂料,给出每个矩形左下角和右上角的两个坐标(x1,y1,x2,y2)。请帮助小蓝计算下粉刷涂料的面积是多少,如果矩形之间有重叠部分只计算一次.
例如:有2个矩形,2个矩形左下角和右上角的两个坐标分别为:(2,2,9,5)、(6,1,12,9),其粉刷涂料的面积是60。

输入描述:

第一行输入一个整数N(2 <= N<= 20),表示有N个矩形
接下来的N行每行包括四个正整数x1, y1,x2,y2( 0 <= x1, y1, x2, y2 <= 100, 且 x1 ≠ x2, y1 ≠ y2),x1和y1表示矩形左下角的坐标,x2和y2表示矩形右上角的坐标,四个正整数之间一个空格隔开。

输出描述:

输出一个整数,表示N个矩形需要粉刷的面积,重叠部分只计算一次

样例输入:

2
2 2 9 5
6 1 12 9

样例输出:

60

/*
解题思路:
    该题可以使用二维数组作为标记数组,先遍历每个矩形的坐标,将其对应的二维数组区域标记为1。
    最后遍历标记数组,累加标记为1的区域面积,即为所求的涂料面积。
*/

#include 
#include 
using namespace std;

// 矩形结构体,包括左下角和右上角坐标
struct Rec {
    int x1, y1, x2, y2;
};
const int N = 110;
bool canvas[N][N];
int main() {
    int n, area = 0;
    Rec Recs[21];
    cin >> n;

    // 读取每个矩形的坐标
    for (int i = 0; i < n; i++) {
        cin >> Recs[i].x1 >> Recs[i].y1 >> Recs[i].x2 >> Recs[i].y2;
    }


    // 遍历每个矩形
    for (int i = 0; i < n; i++) {
    	cout<<"iiiiii"<

蓝桥青少 2021 C++中级 省赛真题(十二届)

选择题

第一题

在C++中下列哪个不属于字符型常量()。
*选择题严禁使用程序验证
A、‘a’ B、‘X2A’ C、’@ D、"F”

解析:字符型常量是用单引号(‘)括起来的单个字符或转义字符,例如’a’或’\n’。选项中,A、B、C都是字符型常量,而D是一个字符串常量,因此不属于字符型常量,正确答案为D。

第二题

以下列变量定义不正确的是()。
*选择题严禁使用程序验证
A、int a=8,b,c; B、float c=1.233; C、int if; D、char d=‘i’;

解析:选项A、B、D都是正确的变量定义方式,而选项C中变量名使用了关键字if,是非法的,正确答案为C。

第三题

已知"int n=9;”, 则执行语句”n*=n+=n%=2;”后,n的值为()。
*选择题严禁使用程序验证
A、4 B、1 C、8 D、18

解析: 根据C++的运算符优先级和结合律,先计算取模运算,再计算加法运算,最后计算赋值运算。因此,n%=2的结果是1,n+=1后n的值为10,n=10后n的值为100,正确答案为D。*

第四题

二进制加法11010+10110的和为()。
*选择题严禁使用程序验证
A、110000 B、11000 C、101110 D、111010

解析:二进制加法的规则与十进制加法类似,逢二进一,结果中每一位都是0或1。在本题中,从低位开始加,1+0=1,0+1=1,1+1=0进1,0+1=1,1+1=0进1,得到的结果为101000,因此正确答案为A。

第五题

C++中函数的返回值类型是由()。
*选择题严禁使用程序验证
A、调用该函数的主调用函数类型决定的
B、return语句中的表达式类型决定的
C、定义该函数所指的数据类型决定的
D、系统自动决定的

解析: C++函数的返回值类型是由函数定义时指定的,一旦确定后就不能更改。在函数定义中使用return语句返回一个值时,该值的类型应与函数返回值类型一致。因此,正确答案为C。

编程题

第一题:编程实现:字符串

题目描述:
给定一个字符串,然后将字符串倒序输出。
输入描述:
输入一个字符串S(2 输出描述:
将字符串S倒序输出

输入样例:
abc

输出样例:
cba

/*
	解题思路:可以使用C++的字符串类string,然后利用循环倒序输出字符串即可。
*/
#include 
#include 

using namespace std;

int main() {
    string s;
    cin >> s; // 输入字符串
    int n = s.length();

    // 倒序输出字符串
    for (int i = n - 1; i >= 0; i--) {
        cout << s[i];
    }
    cout << endl;
    return 0;
}

第二题:编程实现:剪绳子

题目描述:
一条绳子从中间剪一刀可以剪成两段绳子;如果对折1次,中间剪一刀可以剪出3段绳子;如果连续
对折2次,中间剪一刀可以剪出5段绳子;那么,连续对折次,中间剪一刀可以剪出多少段绳子?
通过编写程序,在给定绳子对折次数,计算出中间剪一刀后可剪出绳子的段数。

输入描述:
输入一个正整数n(2 输出描述:
输出一个正整数,表示对折次后的绳子中间剪一刀可以剪出绳子的段数

输入样例:
3
输出样例:
9

解题思路:

通过观察可以发现,第一次对折后,中间剪一刀可以剪出 3 段,第二次对折后,中间剪一刀可以剪出 5 段,以此类推,可得到一个规律:每次对折后,中间剪一刀可以剪出的绳子段数会增加一倍。根据此规律,可以利用递推公式 f ( n ) = 2 n + 1 f(n) = 2^n + 1 f(n)=2n+1,其中 f ( n ) f(n) f(n) 表示对折 n n n 次后,中间剪一刀可以剪出的绳子段数。

#include 
#include 
using namespace std;
int main() {
    int n;
    cin >> n;
    int res = pow(2, n) + 1;
    cout << res << endl;
    return 0;
}

第三题 :编程实现:求和

提示信息:
合数指自然数中除了能被1和它本身整除外,还能被其他数(0除外)整除的数。最小的合数是4。
如:合数4既可以被1和4整除,还能被2整除。

题目描述:
给定一个正整数N,计算出4到N之间所有合数的和。
例如:N等于7,其中4到N之间合数有4、6,所有合数和等于10(4+6=10)

输入描述:
输入一个正整数N(4

输出描述:
输出一个整数,表示4到N之间(包含4和N)所有合数的和
输入样例:
7
输出样例:
10

/*
	题目思路:
    从4到N依次判断每个数是否是合数
    如果是合数,则将其加入到结果中
    判断是否是合数时,可以从2到sqrt(i)依次判断是否能整除
*/

#include 
using namespace std;

// 判断一个数是否为合数
bool isComposite(int n) {
    for (int i = 2; i * i <= n; i++) {
        if (n % i == 0) {
            return true;
        }
    }
    return false;
}

int main() {
    int n, sum = 0;
    cin >> n;

    // 计算4到n之间所有合数的和
    for (int i = 4; i <= n; i++) {
        if (isComposite(i)) {
            sum += i;
        }
    }

    cout << sum << endl;

    return 0;
}

第四题 :编程实现:求和比较

题目描述:
小蓝在学习C++数组时,突发奇想想知道如果将一个连续的正整数数组拆分成两个子数组,然后对拆
分后的两个子数组求和并做差,且差值正好等于一个固定的正整数,像这样同一个连续的正整数数组拆
分方案有多少种。
我们一起帮助小蓝设计一下规则:

第一给出两个正整数N和M;
第二从1到N组成一个连续正整数数组A(A={1,2,3,4……N});
第三将数组A拆分成两个子数组A1、A2(1.拆分的两个子数组中不能出现相同的数;2.子数组中的数字可以是连续的也可以是不连续的;3拆分出的两组子数组的元素个数可以不同,但总数量等于A数组元素个数);
第四对A1、A2两个子数组分别求和;
第五对A1、A2两个子数组的和做差(大的数字减去小的数字);
第六如果差值正好等于固定值M,则判定此拆分方案成立。

如:N=5,M=1,连续正整数数组A={1,2,3,4,5}。

符合条件的拆分方案有3种:
A1={1,2,4},A2={3,5},其中A1的和为7,A2的和为8,两个子数组和的差值等于1
A1={1,3,4},A2={2,5},其中A1的和为8,A2的和为7,两个子数组和的差值等于1
A1={3,4},A2={1,2,5},其中A1的和为7,A2的和为8,两个子数组和的差值等于1

输入描述:
分别输入两个正整数N(3 输出描述:
输出一个正整数,表示1到N(包含1和N)连续的正整数数组中有多少种方案,使得拆分的两个子
数组部分和的差值等于M

输入样例:
5 1
输出样例:
3

/*
	解题思路1:dfs	
	假设 sum(A1) <= sum(A2)
	则题目转换成了从1-i中选数字放入A1 使 sum(A2) - sum(A1) == m
	sum(A2) + sum(A1) = n * (n+1) / 2
	令 sum =  n * (n+1) / 2
	则 sum(A1) = (sum-m)/2   其中 sum-m 必须是偶数才有解 
	
*/
#include 
using namespace std;

int n, m, sum;
int ans;

void dfs(int cur, int sum1, int m) {

	if(sum1 > (sum - m) / 2)  return; //当前集合中数字多了 
	if(sum1 == (sum - m) / 2 ) {
		ans++; 
		return; // 任务成功 后面数字不选 
	} 
	if(cur == n+1) return; 
  
    dfs(cur + 1, sum1 + cur, m ); // 取cur
    dfs(cur + 1, sum1, m); // 不取cur 
}

int main() {
    cin >> n >> m;
    sum = n * (n+1) /2;
    if(m > sum || (sum - m) % 2 != 0){
    	cout << 0;
		return 0; 
	}
    dfs(1, 0, 0); // 从第一个数开始搜索
    cout << ans << endl;
    return 0;
}


/*
	解题思路2 :0/1 背包
	可以用0/1背包来做这道题。可以将原数组分为两个子数组,使得它们的和的差值等于M,即
	sum(A2) - sum(A1) = M
	两个集合的和是 n * (n + 1) / 2;
	则 sum(A1) = { n * (n + 1) / 2 - M } /2;   大括号内部分必须是整数 
	
	那么问题就变成了从 1-n中选数字 ,求 数字和为 { n * (n + 1) / 2 - M } /2 的方案数。
	
	状态表示:dp[i][j] 代表 前i个数字组成j的方案数 
	状态转移: dp[i][j] = dp[i-1][j] + dp[i-1][j-i];  //不选i 选i两种 方案 
*/
#include 
using namespace std;

int n, m, sum, dp[30][510];//dp[i][j] 代表 前i个数字组成j的方案数 
int ans;
int main() {
    cin >> n >> m;
    sum = n * (n + 1) / 2;
    if(m > sum || (sum - m) % 2 != 0){
    	cout << 0;
		return 0; 
	}
   
   	sum = (sum - m) / 2;//子集要求的和
	
	for(int i=0;i<=n;i++)
		dp[i][0]=1;
	for(int i=1;i<=n;i++)
		for (int j=1;j<=sum;j++)
			if (j >= i)
				dp[i][j] = dp[i-1][j] + dp[i-1][j-i];
			else
				dp[i][j] = dp[i-1][j];
	
	cout << dp[n][sum];
   
   
    return 0;
}




第五题 :编程实现:最大价值

题目描述:
一名种菜的农民伯伯,需要在给定的时间内完成种菜,现有种不同的蔬菜提供给农民伯伯选择,且
每种蔬菜种植花费的时间不同,每种蔬菜成熟后售卖的价值也不同。
要求:
1.在限定的总时间内进行蔬菜种植,并且种植蔬菜的种类不能超出限制的数量;
2.选择最优的种植方案使得蔬菜成熟后售卖的总价值最大(可选择不同的蔬菜种植)。
例如:
给定的总时间限制为55,种植蔬菜的种类限制为3;

3种蔬菜,种菜的花费时间及售卖价格分别为:第一种21和9,第二种20和2,第三种30和21。
最优的种植方案是选择种植第一种和第三种,两种蔬菜种植总时间30+21,未超过总时间限制55。所
种植蔬菜为两种,也未超过种类限制的3种。最大总价值为9+21=30,这个方案是最优的。

输入描述:
第一行输入两个正整数t(1 ≤ t ≤ 600)和m(1 ≤ m ≤ 50),用一个空格隔开,t代表种菜总时间限
制,代表最多可种植蔬菜种类的限制

接下来的m行每行输入两个正整数t1(1 < t1 < 101)和p(1 < p < 101)且用一个空格隔开,t1表
示每种蔬菜种植需要花费的时间,p表示对应蔬菜成熟后售卖的价值。
输出描述:
输出一个正整数,表示选择最优的种植方案后,蔬菜成熟后售卖的最大总价值

输入样例:
55 3
21 9
20 2
30 21
输出样例:
30

/*
解题思路:dp
	这是一个典型的动态规划问题,具体的解题思路如下:

    创建一个大小为t+1的数组dp,用于存储每个时间点的最大价值。
    遍历每种蔬菜,对于每种蔬菜,从时间t开始向前遍历到花费时间t1,更新dp数组。
    更新dp的方式:dp[i] = max(dp[i], dp[i-t1] + p),表示在种植这种蔬菜的情况下,时间点i的最大价值等于不种植这种蔬菜的最大价值和种植这种蔬菜的最大价值之间的较大值。
    最后,dp[t]即为在时间限制t内的最大价值。
*/ 
#include 
#include 

using namespace std;

const int MAX_T = 601;
const int MAX_M = 51;

int main() {
    int t, m;
    // 输入种菜总时间限制和种植蔬菜种类的限制
    cin >> t >> m;

    int vegetables[MAX_M][2];
    // 输入每种蔬菜种植花费的时间和售卖价值
    for (int i = 0; i < m; i++) {
        cin >> vegetables[i][0] >> vegetables[i][1];
    }

    int dp[MAX_T] = {0};

    // 遍历蔬菜,计算每种蔬菜对应的最大价值
    for (int i = 0; i < m; i++) {
        int cost = vegetables[i][0];
        int value = vegetables[i][1];
        for (int j = t; j >= cost; j--) {
            // 更新最大价值
            dp[j] = max(dp[j], dp[j - cost] + value);
        }
    }

    // 输出最大总价值
    cout << dp[t] << endl;

    return 0;
}

第六题:编程实现:最短距离

题目描述:
在一个矩阵精灵王国里有两个精灵,一个叫黑精灵,一个叫白精灵。他们住在一个N*M的矩阵方格中
的不同位置,黑精灵住在矩阵方格的左上角方格里(1,1),白精灵住在矩阵方格的右下角方格里(N,M)。

在这个矩阵方格里还有一对可穿越的门,这对穿越门的位置不固定,位置可变换(穿越门不会出现在矩
阵方格左上角和右下角位置,也不会重叠出现,有且只有一对)。穿越门的功能是当进入其中一扇门的
位置后可直接穿越到另一扇门的位置。

如下图所示:
蓝桥杯青少C++省赛备赛指南和真题详解_第5张图片
一天黑精灵要去白精灵家做客,需要穿过方格矩阵到达白精灵家,穿行矩阵方格要求:
1.每次只能走一个方格,可以向上、向下、向左、向右行走;
2.每走一个方格计为1步,但从一扇穿越门穿越到另一扇穿越门不记步数(从方格走到穿越门和从穿
越门到其他方格都计1步);
3.可借助穿越门去白精灵家(可减少行走步数)。
为了尽快到达白精灵家,请你帮助黑精灵找一条最短路线,并且计算出最短路线的步数。

例如:
给出一个3*4矩阵方格,并给出第一个穿越门的坐标位置N1,M1(2,3),,第二个穿越门的坐标位
置N2,M2(3,1),已知黑精灵初始坐标位置左上角(1,1),白精灵坐标位置右下角(N,
M)
假设用两个大写字母“D”表示矩阵方格中穿越门位置,1代表黑精灵,2代表白精灵,用数字0表示
剩余矩阵方格。
如下图所示:

蓝桥杯青少C++省赛备赛指南和真题详解_第6张图片

按照穿行矩阵方格要求为左上角方格的黑精灵到右下角方格白精灵家找一条最短路线,计算出最短路线
的步数。

路线:从黑精灵初始位置(1,1)到正下方方格(2,1)走1步,正下方方格(2,1)到其下方穿越门(3,1)“D”走1步,然后穿越到另一扇穿越门(2,3)向正下方(3,3)走1步,最后到达白精灵家(3,4)需要走1步,故最短路线需要4步。

输入描述:
第一行输入两个以一个空格隔开的正整数N(2

接下来第二行输入两个以一个空格隔开的正整数:N1(N1≤N),M1(M1≤M),代表第一个穿越门位于第N1行第M1列;

接下来第三行输入两个以一个空格隔开的正整数:N2(N2≤N),M2(M2≤M),代表第二个穿越门位于第N2行第M2列;

注意:两个穿越门位置不能重叠,即不能同时满足N1=N2和M1=M2;两个穿越门位置也不能位于左上角(1,1)和右下角(M,N);第一个穿越门位置要在第二个穿越门前边或者上边。

输出描述:
输出一个整数,表示黑精灵去白精灵家最短路线需要走多少步(可借助穿越门,减少步数)。

输入样例:

3 4
2 3
3 1

输出样例:
4

/*
解题思路:
	理清题意后 我们会发现:
		最优方案是从距离终点较远的传送门 传送到距离终点较近的传送门。其余路线的长度就是曼哈顿距离。
		而使用传送门对结果的影响就是缩减了一些距离
		假设传送门1到重点的距离为dis1 传送门2到重点的距离为dis2 
		则使用传送门减少的步数为 abs(dis1-dis2)  abs表示求绝对值
		所以结果为 N + M - 2 - abs(dis1-dis2)
*/
#include 
#include 

using namespace std;

int main() {
    int N, M, N1, M1, N2, M2;
    // 输入矩阵大小和穿越门位置
    cin >> N >> M >> N1 >> M1 >> N2 >> M2;
	
    int dis1 = abs(N1-N) + abs(M1 - M);
    int dis2 = abs(N2-N) + abs(M2 - M);
	
    // 计算使用穿越门的情况下从黑精灵走到白精灵的步数
    int result = N + M - 2 - abs(dis1 - dis2);

    // 输出最短步数
    cout << result << endl;

    return 0;
}

蓝桥杯青少C++省赛备赛指南和真题详解_第7张图片

也欢迎大家登录 趣信奥.com 在线练习蓝桥杯青少C++省赛备赛指南和真题详解_第8张图片

你可能感兴趣的:(蓝桥杯,c++,算法)