2014年第五届蓝桥杯C/C++B组省赛题目及答案2

06. 奇怪的分式

上小学的时候,小明经常自己发明新算法。一次,老师出的题目是:

1/4 乘以 8/5

小明居然把分子拼接在一起,分母拼接在一起,答案是:18/45 (参见图1.png)

老师刚想批评他,转念一想,这个答案凑巧也对啊,真是见鬼!

对于分子、分母都是 1~9 中的一位数的情况,还有哪些算式可以这样计算呢?

请写出所有不同算式的个数(包括题中举例的)。

显然,交换分子分母后,例如:4/1 乘以 5/8 是满足要求的,这算做不同的算式。

但对于分子分母相同的情况,2/2 乘以 3/3 这样的类型太多了,不在计数之列!

注意:答案是个整数(考虑对称性,肯定是偶数)。请通过浏览器提交。不要书写多余的内容。
2014年第五届蓝桥杯C/C++B组省赛题目及答案2_第1张图片

答案:14

ps:要记住gcd函数怎么写(获得最大公约数),写完之后设置输出数据自行检验

代码:

#include 
using namespace std;

int gcd(int a, int b) //这个函数要记得!!!!! 
{
	if (b == 0)
	{
		return a;
	}
	return gcd (b,a%b);
 } 

int main()
{
	int cont = 0;
	double ans = 0;

	for (double i = 1; i <= 9; i++)
	{
		for (double j = 1; j <= 9; j++)
		{
			if (i == j)
			{
				continue;
			}
			for (double k = 1; k <= 9; k++)
			{
				for (double t = 1; t <= 9; t++)
				{
					if (k == t)
					{
						continue;
					}
//					double fenzi = i*10+k;
//					double fenmu = j*10+t;
//					ans = (i/j) * (k/t);
//					if (ans == (fenzi/fenmu))
//					{
//						cont++;
//					}
//上面是错的
					int g1 = gcd(i*k,j*t);  //正确结果的最大公约数 
					int g2 = gcd(i*10+k,j*10+t);  //凑出来的结果的最大公约数
					if (i*k/g1 == (i*10+k)/g2 && j*t/g1 == (j*10+t)/g2)  //两个数约分之后分子分母相同 
					{
//						cout << i << " " << j << " " << k << " " << t << endl; //可以在这里输出每个数自行计算检验 
						cont++;
					}
				} 
			}
		}
	}
	cout << cont << endl;
	return 0;
}

07. 六角填数

如图所示六角形中,填入1~12的数字。

使得每条直线上的数字之和都相同。

图中,已经替你填好了3个数字,请你计算星号位置所代表的数字是多少?

请通过浏览器提交答案,不要填写多余的内容。
2014年第五届蓝桥杯C/C++B组省赛题目及答案2_第2张图片

答案:10

无脑解法:

#include 
using namespace std;

int main()
{
	int arr[10][10];
	arr[0][0] = 1;
	arr[1][0] = 8;
	arr[4][0] = 3;
	for (int a = 1; a <= 12; a++)
	{
		arr[1][1] = a;
		if (a != 1 && a != 8 && a!= 3)
		{
			for (int b = 1; b <= 12; b++)
			{
				arr[1][2] = b;
				if (b != 1 && b != 8 && b!= 3 && b!= a)
				{
					for (int c = 1; c <= 12; c++)
					{
						arr[1][3] = c;
						if (c != 1 && c != 8 && c!= 3 && c!= a && c!= b)
						{
							for(int d = 1; d <= 12; d++)
							{
								arr[2][0] = d;
								if (d!=1 && d!=8 && d!=3 && d!=a && d!=b && d!=c)
								{
									for (int e = 1; e <= 12; e++)
									{
										arr[2][1] = e;
										if (e!=1 && e!=8 && e!=3 && e!=a && e!=b && e!=c && e!=d)
										{
											for (int f = 1; f <= 12; f++)
											{
												arr[3][0] = f;
												if (f!=1 && f!=8 && f!=3 && f!=a && f!=b && f!=c && f!=d && f!=e)
												{
													for (int g = 1; g <= 12; g++)
													{
														arr[3][1] = g;
														if (g!=1 && g!=8 && g!=3 && g!=a && g!=b && g!=c && g!=d && g!=e && g!=f)
														{
															for (int h = 1; h <= 12; h++)
															{
																arr[3][2] = h;
																if (h!=1 && h!=8 && h!=3 && h!=a && h!=b && h!=c && h!=d && h!=e && h!=f && h!=g)
																{
																	for (int i = 1; i <= 12; i++)
																	{
																		arr[3][3] = i;
																		if (i!=1 && i!=8 && i!=3 && i!=a && i!=b && i!=c && i!=d && i!=e && i!=f && i!=g && i!=h)
																		{
																			int l1 = arr[0][0] + arr[1][1] + arr[2][0] + arr[3][0];
																			int l2 = arr[0][0] + arr[1][2] + arr[2][1] + arr[3][3];
																			int l3 = arr[3][0] + arr[3][1] + arr[3][2] + arr[3][3];
																			int l4 = arr[1][0] + arr[1][1] + arr[1][2] + arr[1][3];
																			int l5 = arr[1][0] + arr[2][0] + arr[3][1] + arr[4][0];
																			int l6 = arr[1][3] + arr[2][1] + arr[3][2] + arr[4][0];
																			if (l1 == l2 && l2== l3 && l3== l4 && l4== l5 && l5== l6) //注意没有连等的写法 
																			{
																				cout << arr[2][0] << endl;
																				cout << arr[1][1] << " " << arr[1][2] << " " << arr[1][3] << " " << arr[2][0] << " " << arr[2][1] << " " << arr[3][0] << " " << arr[3][1] << " " << arr[3][2] << " " << arr[3][3] << endl;
																			}
																		}
																	}
																}
															}
														}
													}
												}
											}
										}
									}
								}
							}
						}
					}
				}
			}
		}
	}
	cout << "end" << endl; 
	return 0;
}

2014年第五届蓝桥杯C/C++B组省赛题目及答案2_第3张图片

全排列解决:

#include 
#include 
#include 
using namespace std;

void check(vector<int> v)
{
	int l1 = 1 + v[0] + v[3] + v[5];
	int l2 = 1 + v[1] + v[4] + v[8];
	int l3 = v[5] + v[6] + v[7] +v[8];
	int l4 = 8+ v[0] + v[1] + v[2];
	int l5 = 11 + v[3] + v[6];
	int l6 = 3 + v[2] + v[4] + v[7];
	if (l1==l2 && l2==l3 && l3==l4 && l4==l5 && l5==l6)
	{
		for (int i=0; i<9; i++)
		{
			cout << v[i] << " " << endl;
		} 
	}
}

int main()
{
	vector<int> v;
	//已有的1、8、3不放进去
	v.push_back(2);
	for (int i = 4; i <= 7; i++)
	{
		v.push_back(i);
	} 
	for (int i = 9; i <= 12; i++)
	{
		v.push_back(i);
	} 
	do
	{
		check(v);
	}while(next_permutation(v.begin(),v.end())); //next_permutation是全排列函数,头文件是 
	return 0; 
}

2014年第五届蓝桥杯C/C++B组省赛题目及答案2_第4张图片

08. 蚂蚁感冒

长100厘米的细长直杆子上有n只蚂蚁。它们的头有的朝左,有的朝右。

每只蚂蚁都只能沿着杆子向前爬,速度是1厘米/秒。

当两只蚂蚁碰面时,它们会同时掉头往相反的方向爬行。

这些蚂蚁中,有1只蚂蚁感冒了。并且在和其它蚂蚁碰面时,会把感冒传染给碰到的蚂蚁。

请你计算,当所有蚂蚁都爬离杆子时,有多少只蚂蚁患上了感冒。

【数据格式】

第一行输入一个整数n (1 < n < 50), 表示蚂蚁的总数。

接着的一行是n个用空格分开的整数 Xi (-100 < Xi < 100),Xi的绝对值,表示蚂蚁离开杆子左边端点的距离。正值表示头朝右,负值表示头朝左,数据中不会出现0值,也不会出现两只蚂蚁占用同一位置。其中,第一个数据代表的蚂蚁感冒了。

要求输出1个整数,表示最后感冒蚂蚁的数目。

例如,输入:
3
5 -2 8
程序应输出: 1

再例如,输入:
5
-10 8 -20 12 25
程序应输出: 3

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

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

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

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

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

思路:首先看完问题就知道是需要画图的,然后通过例子来分析:
2014年第五届蓝桥杯C/C++B组省赛题目及答案2_第5张图片

#include 
using namespace std;

int main()
{
	int n;
	cin >> n;
	int arr[50];
	for(int i = 0; i < n; i++)
	{
		cin >> arr[i];
	}
	
	int x = arr[0]; //第一个数据代表的蚂蚁感冒了,x是初始感冒的蚂蚁的位置 
	int ans = 1; 
	
	if (x > 0) //初始感冒蚂蚁是向右的 
	{
		for (int i = 0; i < n; i++) //遍历所有蚂蚁 
		{
			if (arr[i]<0 && -arr[i]>x) //与初始感冒蚂蚁反向且位于初始感冒蚂蚁右边的蚂蚁 
			{
				ans++; 
			} 
		}
		if (ans != 1) //存在和初始感冒蚂蚁对头的,相互传染后,转向,才会去传染一开始的时候和初始感冒蚂蚁同向的 
		{
			for (int i = 0; i < n; i++) //遍历所有蚂蚁 
			{
				if (arr[i]>0 && arr[i]<x) //与初始感冒蚂蚁同向且位于初始感冒蚂蚁左边的蚂蚁 
				{
					ans++; 
				} 
			}
		}
		cout << ans << endl; //初始感冒蚂蚁向右还是向左每次只能有一种情况,所以到这里就能输出答案了 
	}
	
	if (x < 0) //初始感冒蚂蚁是向左的 
	{
		for (int i = 0; i < n; i++) //遍历所有蚂蚁 
		{
			if (arr[i]>0 && arr[i]<-x) //与初始感冒蚂蚁反向且位于初始感冒蚂蚁左边的蚂蚁 
			{
				ans++; 
			} 
		}
		if (ans != 1) //存在和初始感冒蚂蚁对头的,相互传染后,转向,才会去传染一开始的时候和初始感冒蚂蚁同向的 
		{
			for (int i = 0; i < n; i++) //遍历所有蚂蚁 
			{
				if (arr[i]<0 && -arr[i]>-x) //与初始感冒蚂蚁同向且位于初始感冒蚂蚁右边的蚂蚁 
				{
					ans++; 
				} 
			}
		}
		cout << ans << endl; //初始感冒蚂蚁向右还是向左每次只能有一种情况,所以到这里就能输出答案了 
	}  
	return 0;
}

09. 地宫取宝

X 国王有一个地宫宝库。是 n x m 个格子的矩阵。每个格子放一件宝贝。每个宝贝贴着价值标签。

地宫的入口在左上角,出口在右下角。

小明被带到地宫的入口,国王要求他只能向右或向下行走。

走过某个格子时,如果那个格子中的宝贝价值比小明手中任意宝贝价值都大,小明就可以拿起它(当然,也可以不拿)。

当小明走到出口时,如果他手中的宝贝恰好是k件,则这些宝贝就可以送给小明。

请你帮小明算一算,在给定的局面下,他有多少种不同的行动方案能获得这k件宝贝。

【数据格式】

输入一行3个整数,用空格分开:n m k (1<=n,m<=50, 1<=k<=12)

接下来有 n 行数据,每行有 m 个整数 Ci (0<=Ci<=12)代表这个格子上的宝物的价值

要求输出一个整数,表示正好取k个宝贝的行动方案数。该数字可能很大,输出它对 1000000007 取模的结果。

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

再例如,输入:
2 3 2
1 2 3
2 1 5
程序应该输出:
14

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

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

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

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

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

分析:看到n x m就可以猜测基本上是搜索问题(另:如果总价值有限制就是背包问题)
(也类似13年B组的39级台阶,只不过走台阶是每步都会变化(步数+1),但在这里如果不拿,物品数就不变)
所以要考虑的有:深搜、递归、取模

基础答案(下面这个只能得部分分,当规模接近50×50时会超时):

#include 
using namespace std;

const int MOD = 1000000007;
int n,m,k;
int data[50][50];
long long ans;

void dfs(int x, int y, int max, int cnt) //x、y是坐标,max是最大价值,cnt是物品计数 
{
	int cur = data[x][y];
	
	//以下是边界:
	if (x==n || y==m)
		return;  //走完了,退出 
	if (x==n-1 && y==m-1) //已经面临最后一个格子 
	{
		if (cnt==k || (cnt==k-1 && cur>max)) 
		{
			ans++;
			if (ans>MOD)
			{
				ans %= MOD;
			}
		}
	} 
	
	//以下是递归部分: 
	if (cur > max)
	{
		//取这个物品 
		dfs(x,y+1,cur,cnt+1); //往下走
		dfs(x+1,y,cur,cnt+1); //往右走 
	}
	//对于价值较小,或者价值大但不取这个物品的情况如下:
	dfs(x,y+1,max,cnt);  //往下走
	dfs(x+1,y,max,cnt); //往右走 
} 

int main()
{
	cin >> n >> m >> k;
	for (int i=0; i<n; i++)
	{
		for (int j=0; j<m; j++)
		{
			cin >> data[i][j];
		}
	}
	
	dfs(0,0,-1,0); //max取-1是因为第一个点的价值可能是0,如果我们直接赋0的话会少一种可能性
	cout << ans << endl;
	return 0; 
}

优化思路:
先看边界,可以加一个cnt>k时也要return
按以上解法,每个格子会衍生4个分支走法,思考怎样减少分支
如果k很小,每一个格子都要去列出向右还是向下走的结果,就很浪费时间
超时的话就分析,看能不能变成贪心 → (×)
考虑解决重复子问题 (dfs()里的参数相同时就是重复子问题,会重复求解):
出现重复子问题的原因是,每到一个格子,到达这个格子的路径是多样的,可能是从上往下到的,可能是从左往右到的,可能是右下右、下右右等等,最终都是到了同一个格子,此时的x、y是相同的,通过不同路径到这个格子时,max、cnt可能是不同的,也可能是相同的,相同的时候就重复了
或者考虑能不能改成动态规划,用递推的方式解决重复子问题:动态规划可以用动归数组逐步生成(dp),可以用记忆性递归(memory)
这里用记忆性递归优化:

#include 
#include 
using namespace std;

const int MOD = 1000000007;
int n,m,k;
int data[50][50];
long long ans;
long long cache[50][50][14][13];

void dfs(int x, int y, int max, int cnt) //x、y是坐标,max是最大价值,cnt是物品计数 
{
	int cur = data[x][y];
	
	//以下是边界:
	if (x==n || y==m || cnt>k)
		return;  //走完了,退出 
	if (x==n-1 && y==m-1) //已经面临最后一个格子 
	{
		if (cnt==k || (cnt==k-1 && cur>max)) 
		{
			ans++;
			if (ans>MOD)
			{
				ans %= MOD;
			}
		}
	} 
	
	//以下是递归部分: 
	if (cur > max)
	{
		//取这个物品 
		dfs(x,y+1,cur,cnt+1); //往下走
		dfs(x+1,y,cur,cnt+1); //往右走 
	}
	//对于价值较小,或者价值大但不取这个物品的情况如下:
	dfs(x,y+1,max,cnt);  //往下走
	dfs(x+1,y,max,cnt); //往右走 
} 

long long dfs2(int x, int y, int max, int cnt) //x、y是坐标,max是最大价值,cnt是物品计数 
{
	int cur = data[x][y];
	long long ans = 0;
	//先查缓存 
	if (cache[x][y][max+1][cnt] != -1)
	{
		return cache[x][y][max+1][cnt];
	}
	//以下是边界:
	if (x==n || y==m || cnt>k)
		return 0;  //走完了,退出 
	if (x==n-1 && y==m-1) //已经面临最后一个格子 
	{
		if (cnt==k || (cnt==k-1 && cur>max)) 
		{
			ans++;
			if (ans>MOD)
			{
				ans %= MOD;
			}
			return ans;
		}
	} 
	
	//以下是递归部分: 
	if (cur > max)
	{
		//取这个物品 
		ans += dfs2(x,y+1,cur,cnt+1); //往下走
		ans += dfs2(x+1,y,cur,cnt+1); //往右走 
	}
	//对于价值较小,或者价值大但不取这个物品的情况如下:
	ans += dfs2(x,y+1,max,cnt);  //往下走
	ans += dfs2(x+1,y,max,cnt); //往右走 
	
	cache[x][y][max+1][cnt] = ans%MOD; 
	return ans%MOD;
} 

int main()
{
	cin >> n >> m >> k;
	for (int i=0; i<n; i++)
	{
		for (int j=0; j<m; j++)
		{
			cin >> data[i][j];
		}
	}
	
//	dfs(0,0,-1,0); //max取-1是因为第一个点的价值可能是0,如果我们直接赋0的话会少一种可能性
//	cout << ans << endl;

	memset(cache,-1,sizeof(cache));
	cout << dfs2(0,0,-1,0) << endl;
	return 0; 
}

10. 小朋友排队

n 个小朋友站成一排。现在要把他们按身高从低到高的顺序排列,但是每次只能交换位置相邻的两个小朋友。

每个小朋友都有一个不高兴的程度。开始的时候,所有小朋友的不高兴程度都是0。

如果某个小朋友第一次被要求交换,则他的不高兴程度增加1,如果第二次要求他交换,则他的不高兴程度增加2(即不高兴程度为3),依次类推。当要求某个小朋友第k次交换时,他的不高兴程度增加k。

请问,要让所有小朋友按从低到高排队,他们的不高兴程度之和最小是多少。

如果有两个小朋友身高一样,则他们谁站在谁前面是没有关系的。

【数据格式】

输入的第一行包含一个整数n,表示小朋友的个数。 第二行包含 n 个整数 H1 H2 … Hn,分别表示每个小朋友的身高。
输出一行,包含一个整数,表示小朋友的不高兴程度和的最小值。

例如,输入:
3
3 2 1
程序应该输出:
9

【样例说明】 首先交换身高为3和2的小朋友,再交换身高为3和1的小朋友,再交换身高为2和1的小朋友,每个小朋友的不高兴程度都是3,总和为9。

【数据规模与约定】 对于10%的数据, 1<=n<=10; 对于30%的数据, 1<=n<=1000; 对于50%的数据,
1<=n<=10000; 对于100%的数据,1<=n<=100000,0<=Hi<=1000000。

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

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

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

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

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

首先先学习一下树状数组(要理解性背下来模板):
2014年第五届蓝桥杯C/C++B组省赛题目及答案2_第6张图片
2014年第五届蓝桥杯C/C++B组省赛题目及答案2_第7张图片
2014年第五届蓝桥杯C/C++B组省赛题目及答案2_第8张图片
2014年第五届蓝桥杯C/C++B组省赛题目及答案2_第9张图片

#include 
#include 
using namespace std;

int lowbit(int n)
{
	return n-(n&(n-1));
}

/**原始数组的i位置增加v后,更新c数组,n是边界 
 * @param n
 * @param i
 * @param v
 * @param c
 */
  
void updata (int n, int i, int v, int c[])
{
	int lb = lowbit(i); //求出转成二进制之后最低位的1所代表的的整数
	for (int k = i; k <= n; k += lowbit(k)) //就是逐个加,例如C9+1(看图解) 
	{
		c[k] += v;
	}
} 

int getsum(int c[], int i)
{
	int sum = 0;
	for (int k = i; k >= 1; k -= lowbit(k)) //就是往前扣,例如C12是1100,往前扣4,得C8,是1000,往前扣8,得C0,结束 
	{
		sum += c[k];
	}
	return sum;
}

int main()
{
	int arr[] = {1,2,3,4,5,6,7,8};
	int c[9];
	memset(c,0,sizeof(c));
	for (int i = 0; i < 8; i++)
	{
		updata(9, i+1, arr[i], c);
	}
	cout << getsum(c,5) <<endl;
	cout << getsum(c,6) << endl;
	cout << getsum(c,7) - getsum(c,5) << endl;
	return 0;
}

下面解决本题:
2014年第五届蓝桥杯C/C++B组省赛题目及答案2_第10张图片
2014年第五届蓝桥杯C/C++B组省赛题目及答案2_第11张图片
2014年第五届蓝桥杯C/C++B组省赛题目及答案2_第12张图片

#include 
#include 
using namespace std;

int lowbit(int n)
{
	return n-(n&(n-1));
}

/**原始数组的i位置增加v后,更新c数组,n是边界 
 * @param n
 * @param i
 * @param v
 * @param c
 */
  
void updata (int n, int i, int v, int c[])
{
	int lb = lowbit(i); //求出转成二进制之后最低位的1所代表的的整数
	for (int k = i; k <= n; k += lowbit(k)) //就是逐个加,例如C9+1(看图解) 
	{
		c[k] += v;
	}
} 

int getsum(int c[], int i)
{
	int sum = 0;
	for (int k = i; k >= 1; k -= lowbit(k)) //就是往前扣,例如C12是1100,往前扣4,得C8,是1000,往前扣8,得C0,结束 
	{
		sum += c[k];
	}
	return sum;
}

int h[100000];
long long cnt[100000]; //记录每个孩子的交换次数 
int c[1000000+1]; //因为我们是用身高做下标,不是孩子的数目做下标 
 
int main()
{
	int n;
	cin >> n;
	memset (cnt,0,sizeof(cnt)); //初始化 
	int maxH = -1;	
	for (int i = 0; i < n; i++)
	{
		cin >> h[i];
		if (h[i] > maxH)
		{
			maxH = h[i];
		}
	} 
	memset (c,0,sizeof(c));
	
	//下面从左往右开始扫,扫到的数字作为下标,执行updata
	for (int i = 0; i < n; i++)
	{
		updata(maxH+1,h[i]+1,1,c); //在相应位置计数变为1,其实就是用树状数组维护数据出现的个数 
		int sum = getsum(c,h[i]+1); //小于等于h[i]+1的数据的个数
		cnt[i] += (i+1)-sum; //得到的就是当前数据左侧比数据大的数的个数 
	} 
	//下面开始从右往左扫 
	memset (c,0,sizeof(c));
	for (int i = n-1; i >= 0; i--)
	{
		updata(maxH+1,h[i]+1,1,c); //在相应位置计数变为1,其实就是用树状数组维护数据出现的个数 
//		int sum = getsum(c,h[i]+1); //小于等于h[i]+1的数据的个数
//		int self = getsum(c,h[i]+1) - getsum(c,h[i]); 
//		cnt[i] += sum-self; //上一步求出小于等于h的个数,扣掉自己的个数,得到的就是当前数据右侧比数据小的数的个数
		//以上三行综合如下:
		cnt[i] += getsum(c,h[i]); //求出小于h[i]+1的数据的个数 
	} 
	
	long long ans = 0;
	for (int i = 0; i < n; i++)
	{
		ans += (cnt[i]*(cnt[i]+1)/2); //把交换次数转化成不高兴值 
	}
	cout << ans << endl;
	return 0;
}

你可能感兴趣的:(#,蓝桥杯刷题)