第七届蓝桥杯c/c++ B组省赛真题总结

1.煤球数目

有一堆煤球,堆成三角棱锥形。具体:
第一层放1个,
第二层3个(排列成三角形),
第三层6个(排列成三角形),
第四层10个(排列成三角形),

如果一共有100层,共有多少个煤球?

请填表示煤球总数目的数字。
注意:你提交的应该是一个整数,不要填写任何多余的内容或说明性文字。

【思路】
找规律,f(n) = f(n - 1) + n

代码:

/*
推导发现,第一层是+ 2得到第二层,第二层加3得到第三层
所以第100层 = 99层的 + 100 

结果:171700 
*/
#include<iostream>
using namespace std;

const int maxn = 105;
int dp[maxn];

int DP()
{
	dp[1] = 1;
	for(int i = 2;i <= 100;i++)
	{
		dp[i] = dp[i - 1] + i;
	}
	int sum =0 ;
	for(int i = 1;i <= 100;i++)
	{
		sum += dp[i];
	}
	//print  dp
	cout << "dp : ";
	for(int i = 1;i <= 100;i++)
	{
		cout << dp[i] << " ";
	}
	cout << endl;
	return sum;
}

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

2.生日蜡烛

某君从某年开始每年都举办一次生日party,并且每次都要吹熄与年龄相同根数的蜡烛。

现在算起来,他一共吹熄了236根蜡烛。

请问,他从多少岁开始过生日party的?

请填写他开始过生日party的年龄数。
注意:你提交的应该是一个整数,不要填写任何多余的内容或说明性文字。

【思路】
直接暴力枚举他开始过生日的年龄

代码:

#include<iostream>
using namespace std;

int main()
{
	int sum = 0;
	bool flag = true;
	for(int i = 1;i <= 100;i++)
	{
		sum = i;
		flag = true;
		for(int j = i + 1;j <= 200 && flag;j++)
		{
			sum += j;
			if(sum > 236)
				flag = false;
			if(sum == 236)
			{
				cout << i << endl;
				break;
			}
		}
	}
	/*
	测试 
	sum = 26;
	cout << sum << endl;
	for(int i = 27;i <= 200;i++)
	{
		sum += i;
		cout << i << endl;
		if(sum == 236)
		{
			break;
		}
	}
	*/
	return 0;
}

答案:26


3.凑算式

  B      DEF

A + — -+ ------- = 10
C GHI

(如果显示有问题,可以参见【图1.jpg】)

这个算式中AI代表19的数字,不同的字母代表不同的数字。

比如:
6+8/3+952/714 就是一种解法,
5+3/1+972/486 是另一种解法。

这个算式一共有多少种解法?

注意:你提交应该是个整数,不要填写任何多余的内容或说明性文字。
第七届蓝桥杯c/c++ B组省赛真题总结

【思路】:
我也用的暴力枚举,直接循环搜。在蓝桥杯中暴力是很常用的方法。那么这里注意一下选一个数就要做一个标记,跟回溯一样,因为不能同一个数选多次。然后再就是DEF它是等于D * 100 + E * 10 + F,记得我刚开始算的时候差点理解成D * E * F。。。

还有一点注意下,这里的除,都必须除尽,跟计算机中取整不同,不然会算出很多结果。

代码:(纯暴力,有点多。。。)

#include<iostream>
using namespace std;

const int maxn = 20;
int tag[maxn];
int ans = 0;

int main()
{
	for(int a = 1;a <= 9;a++)
	{
		tag[a] = 1;
		for(int b = 1;b <= 9;b++)
		{
			if(tag[b] != 1)
			{
				tag[b] = 1;
				for(int c = 1;c <= 9;c++)
				{
					if(tag[c] != 1)
					{
						tag[c] = 1;
						for(int d = 1;d <= 9;d++)
						{
							if(tag[d] != 1)
							{
								tag[d] = 1;
								for(int e = 1;e <= 9;e++)
								{
									if(tag[e] != 1)
									{
										tag[e] = 1;
										for(int f = 1;f <= 9;f++)
										{
											if(tag[f] != 1)
											{
												tag[f] = 1;
												for(int g = 1;g <= 9;g++)
												{
													if(tag[g] != 1)
													{
														tag[g] = 1;
														for(int h = 1;h <= 9;h++)
														{
															if(tag[h] != 1)
															{
																tag[h] = 1;
																for(int i = 1;i <= 9;i++)
																{
																	if(tag[i] != 1)
																	{
																		tag[i] = 1;
																		int x = g * 100 + h * 10 + i;
																		int y = d * 100 + e * 10 + f;
																		int r = b * x + c * y;
																		int z = c * x;
																		if(r % z == 0)
																		{
																			if(a + (r / z) == 10)
																			{
																				ans++;
																				cout<<a<<","<<b<<","<<c<<","<<d<<","<<e<<","<<f<<","<<g<<","<<h<<","<<i<<endl;
																			}
																		}
																		tag[i] = 0;
																	}
																}
																tag[h] = 0;
															}
														}
														tag[g] = 0;
													}
												}
												tag[f] = 0;
											}
										}
										tag[e] = 0;	
									}
								}
								tag[d] = 0;
							}
						}
						tag[c] = 0;	
					}
				}
				tag[b] = 0;	
			}
		}
		tag[a] = 0;
	}
	cout << ans << endl;
	return 0;
}

算出来结果29.


4.快速排序

排序在各种场合经常被用到。
快速排序是十分常用的高效率的算法。

其思想是:先选一个“标尺”,
用它把整个队列过一遍筛子,
以保证:其左边的元素都不大于它,其右边的元素都不小于它。

这样,排序问题就被分割为两个子区间。
再分别对子区间排序就可以了。

下面的代码是一种实现,请分析并填写划线部分缺少的代码。

#include <stdio.h>

void swap(int a[], int i, int j)
{
	int t = a[i];
	a[i] = a[j];
	a[j] = t;
}

int partition(int a[], int p, int r)
{
    int i = p;
    int j = r + 1;
    int x = a[p];
    while(1){
        while(i<r && a[++i]<x);
        while(a[--j]>x);
        if(i>=j) break;
        swap(a,i,j);
    }
	______________________;
    return j;
}

void quicksort(int a[], int p, int r)
{
    if(p<r){
        int q = partition(a,p,r);
        quicksort(a,p,q-1);
        quicksort(a,q+1,r);
    }
}
    
int main()
{
	int i;
	int a[] = {5,13,6,24,2,8,19,27,6,12,1,17};
	int N = 12;
	
	quicksort(a, 0, N-1);
	
	for(i=0; i<N; i++) printf("%d ", a[i]);
	printf("\n");
	
	return 0;
}

注意:只填写缺少的内容,不要书写任何题面已有代码或说明性文字。

【思路】
要你程序填空,这也是我很烦的类型,如果遇到简单的还好,看一看,试一试就可以出来。如果程序本身写得就很复杂,然后又让你根据他的思路来填,那就很烦了!

这题其实还算好填的,要理解题意。你发现partition函数就是找到了一个标尺,以保证其左边的元素都不大于它,其右边的元素都不小于它,然后partition函数中的那个下标p,实际上就像快排中的那个参照点,所以也很好想了,就是swap(a, p, j);


5.抽签

X星球要派出一个5人组成的观察团前往W星。
其中:
A国最多可以派出4人。
B国最多可以派出2人。
C国最多可以派出2人。

那么最终派往W星的观察团会有多少种国别的不同组合呢?

下面的程序解决了这个问题。
数组a[] 中既是每个国家可以派出的最多的名额。
程序执行结果为:
DEFFF
CEFFF
CDFFF
CDEFF
CCFFF
CCEFF
CCDFF
CCDEF
BEFFF
BDFFF
BDEFF
BCFFF
BCEFF
BCDFF
BCDEF

(以下省略,总共101行)


#include <stdio.h>
#define N 6
#define M 5
#define BUF 1024

void f(int a[], int k, int m, char b[])
{
	int i,j;
	
	if(k==N){ 
		b[M] = 0;
		if(m==0) printf("%s\n",b);
		return;
	}
	
	for(i=0; i<=a[k]; i++){
		for(j=0; j<i; j++) b[M-m+j] = k+'A';
		______________________;  //填空位置
	}
}
int main()
{	
	int  a[N] = {4,2,2,1,1,3};
	char b[BUF];
	f(a,0,M,b);
	return 0;
}

仔细阅读代码,填写划线部分缺少的内容。

注意:不要填写任何已有内容或说明性文字。

也是程序填空,但我觉得这道题比前一道简单点。如果能很好的理解全排列、递归,那这一题其实很好填
f(a, k + 1, m - j, b);


6.方格填数

如下的10个格子

(如果显示有问题,也可以参看【图1.jpg】)

填入0~9的数字。要求:连续的两个数字不能相邻。
(左右、上下、对角都算相邻)

一共有多少种可能的填数方案?

请填写表示方案数目的整数。
注意:你提交的应该是一个整数,不要填写任何多余的内容或说明性文字。
第七届蓝桥杯c/c++ B组省赛真题总结
【思路】
这是结果填空题。这题一看就是dfs尝试嘛,我直接用的深搜,这个方格实际上用一个一维数组来存就够了。

代码:

/*
穷举
3404
k == 6写掉一个判断,大意了。。。 
fabs(a[1] - i) != 1

正确答案:1580 
*/
#include<iostream>
#include<cmath>
using namespace std;

const int maxn = 20;
int a[maxn];
int tag[maxn];
int ans = 0;

void dfs(int k)			//k表示格子的序号 
{
	if(k == 11)
	{
		ans++;
		cout << "************" << endl;
		for(int i = 1;i <= 10;i++)
		{
			cout << a[i] << " ";
		}
		cout << endl;
		return ;
	}
	for(int i = 0;i <= 9;i++)
	{
		if(k == 1)
		{
			tag[i] = 1;
			a[k] = i;
			dfs(k + 1);
			tag[i] = 0;
		}
		else if(k == 2)
		{
			if(fabs(a[1] - i) != 1 && tag[i] != 1)
			{
				tag[i] = 1;
				a[k] = i;
				dfs(k + 1);
				tag[i] = 0;
			}
		}
		else if(k == 3)
		{
			if(fabs(a[2] - i) != 1 && tag[i] != 1)
			{
				tag[i] = 1;
				a[k] = i;
				dfs(k + 1);
				tag[i] = 0;
			}
		}
		else if(k == 4)
		{
			if(fabs(a[1] - i) != 1 && tag[i] != 1)
			{
				tag[i] = 1;
				a[k] = i;
				dfs(k + 1);
				tag[i] = 0;
			}
		}
		else if(k == 5)
		{
			if(fabs(a[1] - i) != 1 && fabs(a[4] - i) != 1 && fabs(a[2] - i) != 1
			 && tag[i] != 1)
			{
				tag[i] = 1;
				a[k] = i;
				dfs(k + 1);
				tag[i] = 0;
			}
		}
		else if(k == 6)
		{
			if(fabs(a[2] - i) != 1 && fabs(i - a[5]) != 1 && fabs(a[3] - i) != 1
			 && tag[i] != 1 && fabs(a[1] - i) != 1)
			{
				tag[i] = 1;
				a[k] = i;
				dfs(k + 1);
				tag[i] = 0;
			}
		}
		else if(k == 7)
		{
			if(fabs(a[2] - i) != 1 && fabs(a[6] - i) != 1 && fabs(a[3] - i) != 1
			 && tag[i] != 1)
			{
				tag[i] = 1;
				a[k] = i;
				dfs(k + 1);
				tag[i] = 0;
			}
		}
		else if(k == 8)
		{
			if(fabs(a[4] - i) != 1 && fabs(i - a[5]) != 1  && tag[i] != 1)
			{
				tag[i] = 1;
				a[k] = i;
				dfs(k + 1);
				tag[i] = 0;
			}
		}
		else if(k == 9)
		{
			if(fabs(a[4] - i) != 1 && fabs(i - a[5]) != 1 && fabs(i - a[6]) != 1
			&& fabs(i - a[8]) != 1 && tag[i] != 1)
			{
				tag[i] = 1;
				a[k] = i;
				dfs(k + 1);
				tag[i] = 0;
			}
		}
		else if(k == 10)
		{
			if(fabs(a[5] - i) != 1 && fabs(i - a[6]) != 1 && fabs(i - a[9]) != 1 
			&& fabs(i - a[7]) != 1  && tag[i] != 1)
			{
				tag[i] = 1;
				a[k] = i;
				dfs(k + 1);
				tag[i] = 0;
			}
		}
	}
}
 
int main()
{
	dfs(1);
	cout << ans << endl;
	return 0;
}

很遗憾的是,第一次做的时候k == 6的情况写掉一个条件,导致最后算出来3404.
就是第六个格子应该和第一个格子也有一些限制关系,但我看漏了。正确答案:1580


7.剪邮票

如【图1.jpg】, 有12张连在一起的12生肖的邮票。
现在你要从中剪下5张来,要求必须是连着的。
(仅仅连接一个角不算相连)
比如,【图2.jpg】,【图3.jpg】中,粉红色所示部分就是合格的剪取。

请你计算,一共有多少种不同的剪取方法。

请填写表示方案数目的整数。
注意:你提交的应该是一个整数,不要填写任何多余的内容或说明性文字。
第七届蓝桥杯c/c++ B组省赛真题总结
第七届蓝桥杯c/c++ B组省赛真题总结
第七届蓝桥杯c/c++ B组省赛真题总结
【思路】
这题我刚开始做的时候,也是没有思路,最后没有做出来,因为传统的深搜是不行的,对于图三这种情况,深搜解决不了。

查找思路之后,发现其实很简单啊。。就是一个深搜+一个广搜。我们相当于先对格子的序号进行一个组合,这就需要一个dfs。然后我们再对每一个组合进行bfs,看看它们是不是连在一起的!

蓝桥杯很喜欢考dfs+bfs的组合题目!
虽然思路清楚了,但写代码的时候也是写了半天,因为粗心大意,很多地方写错,导致程序运行结果错误,调试了半天才成功

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

int ans = 0;
int x[6];				//装选好的邮票序号 
struct Node{
	int x;
	int y;
	int num;
	Node(int x, int y, int num)
	{
		this->x = x;
		this->y = y;
		this->num = num;
	}
};
int m[3][4] = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12};
int vis[3][4];
int tag[20];

bool jianzhi(int k, int i)
{
	if(tag[i] == 1)
		return false;
	for(int j = 0;j < k;j++)
	{
		if(x[j] > i)			//保证递增顺序 
			return false;
	}
	return true;
}

bool panduan(int x1, int y, int n)
{
	bool ok = false;
	if(x1 < 0 || y < 0 || x1 > 2 || y > 3)		//这里写错。。。y >2了 
		return false;
	if(vis[x1][y] == 1)
		return false;
	for(int i = 1;i <= 5;i++)
	{
		if(x[i] == n)
		{
			ok = true;
			break;
		}
	}
	return ok;
}

void Clear()
{
	for(int i = 0;i < 3;i++)
	{
		for(int j = 0;j < 4;j++)
		{
			vis[i][j] = 0;
		}
	}
}

bool bfs(int n)
{
	queue<Node> q;
	int x = (n - 1) / 4;
	int y = (n - 1) % 4;
	q.push(Node(x, y, n));
	vis[x][y] = 1;
	int num = 1;			//初始num应该为1,之前错写成0,调错找了半天。。。 
	while(!q.empty())
	{
		Node f = q.front();
		if(panduan(f.x + 1, f.y, m[f.x + 1][f.y]))
		{
			q.push(Node(f.x + 1, f.y, m[f.x + 1][f.y]));
			vis[f.x + 1][f.y] = 1;
			num++;
		}
		if(panduan(f.x, f.y + 1, m[f.x][f.y + 1]))
		{
			q.push(Node(f.x, f.y + 1, m[f.x][f.y + 1]));
			vis[f.x][f.y + 1] = 1;
			num++;
		}
		if(panduan(f.x - 1, f.y, m[f.x - 1][f.y]))
		{
			q.push(Node(f.x - 1, f.y, m[f.x - 1][f.y]));
			vis[f.x - 1][f.y] = 1;
			num++;
		}
		if(panduan(f.x, f.y - 1, m[f.x][f.y - 1]))
		{
			q.push(Node(f.x , f.y - 1, m[f.x][f.y - 1]));
			vis[f.x][f.y - 1] = 1;
			num++;
		}
		q.pop();
	}
	Clear();
	if(num == 5)
		return true;
	else
		return false;
}


void dfs(int k)			
{
	if(k == 6)			//到达边界 
	{
		if(bfs(x[1]))		//用bfs判断,如果这五个邮票是相连的,那么总结果就 + 1 
		{
			ans++;
			for(int i = 1;i <= 5;i++)
			{
				cout << x[i] << " ";
			}
			cout << endl;
		}
		return ;
	}
	//每次枚举不同的邮票序号 
	for(int i = 1;i <= 12;i++)	//这里枚举要递增,确保结果不重复 
	{
		if(jianzhi(k, i))			//如果这个邮票没有被用过 
		{
			tag[i] = 1;
			x[k] = i;
			dfs(k + 1);
			tag[i] = 0;
		}
	}
}

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

关键是思路啊。。。


8.四平方和

四平方和定理,又称为拉格朗日定理:
每个正整数都可以表示为至多4个正整数的平方和。
如果把0包括进去,就正好可以表示为4个数的平方和。

比如:
5 = 0^2 + 0^2 + 1^2 + 2^2
7 = 1^2 + 1^2 + 1^2 + 2^2
(^符号表示乘方的意思)

对于一个给定的正整数,可能存在多种平方和的表示法。
要求你对4个数排序:
0 <= a <= b <= c <= d
并对所有的可能表示法按 a,b,c,d 为联合主键升序排列,最后输出第一个表示法

程序输入为一个正整数N (N<5000000)
要求输出4个非负整数,按从小到大排序,中间用空格分开

例如,输入:
5
则程序应该输出:
0 0 1 2

再例如,输入:
12
则程序应该输出:
0 2 2 2

再例如,输入:
773535
则程序应该输出:
1 1 267 838

资源约定:
峰值内存消耗 < 256M
CPU消耗 < 3000ms

请严格按要求输出,不要画蛇添足地打印类似:“请您输入…” 的多余内容。

所有代码放在同一个源文件中,调试通过后,拷贝提交该源码。

注意: main函数需要返回0
注意: 只使用ANSI C/ANSI C++ 标准,不要调用依赖于编译环境或操作系统的特殊函数。
注意: 所有依赖的函数必须明确地在源文件中 #include , 不能通过工程设置而省略常用头文件。

提交时,注意选择所期望的编译器类型。

【思路】
直接dfs枚举,我个人认为比上一题简单

代码:

/*
暂时只会暴力 
773535
1 1 267 838
3535结尾的会很慢 
*/
#include<iostream>
#include<algorithm>
using namespace std;

bool flag = false;
int x[5];

void dfs(int k, int val)			//val表示现在的值 
{
	if(val == 0 || k == 5)
	{
		if(val == 0)
		{
			for(int i = 1;i <= 4;i++)
			{
				cout << x[i] << " ";
			}
			cout << endl;
			flag = true;
		}
		return ;
	}
	for(int i = 0;i * i <= val;i++)
	{
		if(val - i * i >= 0)
		{
			x[k] = i;
			dfs(k + 1, val - i * i);
			if(flag)
				return ;
		}
	}
}

int main()
{
	int n;
	cin >> n;
	dfs(1, n);
	return 0;
}

9.交换瓶子

有N个瓶子,编号 1 ~ N,放在架子上。

比如有5个瓶子:
2 1 3 5 4

要求每次拿起2个瓶子,交换它们的位置。
经过若干次后,使得瓶子的序号为:
1 2 3 4 5

对于这么简单的情况,显然,至少需要交换2次就可以复位。

如果瓶子更多呢?你可以通过编程来解决。

输入格式为两行:
第一行: 一个正整数N(N<10000), 表示瓶子的数目
第二行:N个正整数,用空格分开,表示瓶子目前的排列情况。

输出数据为一行一个正整数,表示至少交换多少次,才能完成排序。

例如,输入:
5
3 1 2 5 4

程序应该输出:
3

再例如,输入:
5
5 4 3 2 1

程序应该输出:
2

资源约定:
峰值内存消耗 < 256M
CPU消耗 < 1000ms

请严格按要求输出,不要画蛇添足地打印类似:“请您输入…” 的多余内容。

所有代码放在同一个源文件中,调试通过后,拷贝提交该源码。

注意: main函数需要返回0
注意: 只使用ANSI C/ANSI C++ 标准,不要调用依赖于编译环境或操作系统的特殊函数。
注意: 所有依赖的函数必须明确地在源文件中 #include , 不能通过工程设置而省略常用头文件。

提交时,注意选择所期望的编译器类型。

【思路】
这题一开始也是没做出来,想到动态规划上面去了。后来网上查了查,发现竟然真的是贪心。。。选择排序。其实,我也想过,就是直接把不在正确位置的瓶子跟应该放在此位置的瓶子交换,但我不知道如何证明,所以就没做。。。

关于贪心算法的证明,确实挺麻烦的,我看网上也没谁能证出来。反正又给我提供了一种思路哈哈,就是一旦有贪心的思路,就可以去做,一般情况下,可以对一部分数据,总比你空着好吧!

#include<iostream>
using namespace std;

const int maxn = 10005;
int a[maxn];
bool tag = false;
int cnt = 0;

void Greedy(int n)
{
	for(int i = 1;i <= n;i++)
	{
		tag = false;
		if(a[i] == i)	continue;
		else
		{
			for(int j = i;j <= n && !tag;j++)
			{
				if(a[j] == i)
				{
					int temp = a[i];
					a[i] = a[j];
					a[j] = temp;
					tag = true;
					cnt++;
				}
			}
		}
	}
}

int main()
{
	int n;
	cin >> n;
	for(int i = 1;i <= n;i++)
	{
		cin >> a[i];
	}
	Greedy(n);
	cout << cnt << endl;
	return 0;
}

最后一题就先不放了,目前还不会。反正我的战绩是前5题全对,第6题遗憾的错了,第7题没写出来,第8题写出来了,第9题考试时因为不会证明也没写,最后一题没写。大概就是对6题不到的样子。。。

这次真题训练感觉还是很有难度的啊,蓝桥杯备战要抓紧!