2016第七届蓝桥杯省赛c/c++ B组

第一题 :煤球数目

题目叙述: 
有一堆煤球,堆成三角棱锥形。具体: 
第一层放1个, 
第二层3个(排列成三角形), 
第三层6个(排列成三角形), 
第四层10个(排列成三角形), 
…. 
如果一共有100层,共有多少个煤球?

请填表示煤球总数目的数字。 

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

答案:171700

规律比较好找,每层煤球的个数是(1+2+ ... + n) 也就是 (n+1)*n/2;

100层求和即可。

#include <iostream>

using namespace std;

int main()
{
	long long res = 0;
	for ( int i = 1 ; i <= 100 ; ++ i ) {
		res += (i+1)*i/2;
	}
	cout << res << endl;
	return 0;
}

第二题 :生日蜡烛

题目叙述: 
某君从某年开始每年都举办一次生日party,并且每次都要吹熄与年龄相同根数的蜡烛。 
现在算起来,他一共吹熄了236根蜡烛。 
请问,他从多少岁开始过生日party的? 
请填写他开始过生日party的年龄数。 
注意:你提交的应该是一个整数,不要填写任何多余的内容或说明性文字。

答案:26

没什么技巧,直接3层暴力枚举年龄,过生日年份,累加求和,判断和是不是236。

不过问题就是万一,某君长寿咋整,恰好今年236过一次生日。不说了,尴尬症犯了。

#include <iostream>

using namespace std;

int main()
{
	for ( int i = 1 ; i < 236 ; ++ i ) {
		for ( int j = 1 ; j < 236 ; ++ j ) {
			int ans = 0, cnt = i;
			for ( int k = 1 ; k < j ; ++ k ) {
				ans += cnt++;
			} 
			if ( ans == 236 ) {
				cout << i << endl;
				return 0;
			}
		}
	}
	return 0;
}

第三题 :凑算式

题目叙述: 
  B    DEF 
A + — + ——— = 10 
  C    GHI 

这个算式中A~I代表1~9的数字,不同的字母代表不同的数字。

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

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

注意:你提交应该是个整数,不要填写任何多余的内容或说明性文字。

正确答案:29

这里枚举还是有些技巧的,我这里只是对全排列稍微做了一些优化,没有做数学上的分析,比如根据A的范围可以大致确定后两个数的大小,这样可以有效提高算法很多,但是分析过程也很复杂烧脑。

#include <iostream>
#include <algorithm>

using namespace std;

int a[] = {1, 2, 3, 4, 5, 6, 7, 8, 9};

int gcd( int a, int b ) {
	while ( a && b ) a>b?a%=b:b%=a;
	return a+b;
}

int main()
{
	int ans = 0;
	do {
		int n0 = a[0];
		int n1 = a[1], n2 = a[2];
		int n3 = a[3]*100+a[4]*10+a[5];
		int n4 = a[6]*100+a[7]*10+a[8];
		// 先大致算出来结果范围  按照程序除法不留小数 结果应该在 8.xx ~ 10 
		int gus = n0+n1/n2+n3/n4;
		if ( gus > 10 || gus < 8 ) continue;
		// 如果n2 n4互质 则没有通分 直接算结果 
		if ( gcd(n2, n4) == 1 ) {
			if ( n1/n2*n2 != n1 ) continue; 
			if ( n3/n4*n4 != n3 ) continue; 
			if ( gus == 10 ) ans ++; 
		} else { // 否则要通分  这里根据公式把除法转换成了乘法
			// 其实这个式子对是否需要通分都合适 所以直接判断这里就可以得出正确答案 
			if ( n1*n4 == ((10-n0)*n4-n3)*n2 ) {
				ans ++;
			}
		}
	}
	while ( next_permutation(begin(a), end(a)) );
	cout << ans << endl;
	return 0;
}

第四题 :快速排序

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

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

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

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

#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;
}

正确答案:swap(a,p,j)

快排,编程必备算法。


第五题 :抽签

题目叙述: 
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-i,b)  (或者  f(a,k+1,m-i,b) ,因为 i 和 j 相等

似乎每年蓝桥杯省赛都会出一个递归的填空题。


第六题 :方格填数

题目叙述: 
如下的10个格子 

 

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

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

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

答案:1580

#include <iostream>
#include <cmath>

using namespace std;

#define inf 100

int a[3][4];
int res = 0;

bool valid( int x, int y ) {
	if ( x==0 && y==0 ) return false;
	if ( x==2 && y==3 ) return false;
	return x>=0 && x<3 && y>=0 && y<4; 
}

bool ok(int x, int y) {
	for ( int i = -1 ; i <= 1 ; ++ i ) {
		for ( int j = -1 ; j <= 1 ; ++ j ) {
			if ( i == 0 && j == 0 ) continue;
			if ( valid(x+i, y+j) ) {
				if ( abs(a[x+i][y+j] - a[x][y]) == 1 ) return false;
			}
		}
	}	
	return true;
}

void dfs( int k ) {
	if ( k == 10 ) {
		res ++;
	} else {
		for ( int i = 0 ; i < 3 ; ++ i ) {
			for ( int j = 0 ; j < 4 ; ++ j ) {
				if ( a[i][j] == inf && valid(i,j) ) {
					a[i][j] = k;
					if ( ok(i,j) ) dfs( k+1 );
					a[i][j] = inf;
				}
			}
		}
	}
}

int main()
{
	for ( int i = 0 ; i < 3 ; ++ i ) {
		for ( int j = 0 ; j < 4 ; ++ j ) {
			a[i][j] = inf;
		}
	}
	dfs( 0 );
	cout << res << endl;
	return 0;
}


第七题 :剪邮票

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

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

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





这里可能会有一种 对每个点为起点进行深度为5的深搜,然后用set之类的去重。

但是深搜不会拐弯, 所以会少算一些情况,例如:

-   -  o  - 或者     o o o o  
o  o  o  -              -  o -  -
-   o  -  -               -  -  -  - 

圆圈为邮票  -为空白 

深搜的结果应该是82,贴下自己错误的代码,以示警告:

#include <iostream>
#include <set>
#include <string>

using namespace std;

int vis[3][4];
int ans;
set<string> s;
int dx[] = { 1, 0, -1, 0 };
int dy[] = { 0, 1, 0, -1 };

bool valid( int x, int y ) {
	return (x>=0 && x<3 && y>=0 && y<4);
}

void dfs( int x, int y, int step ) {
	if ( step >= 5 ) {
		string str = "";
		for ( int i = 0 ; i < 3 ; ++ i ) {
			for ( int j = 0 ; j < 4 ; ++ j ) {
				if( vis[i][j] ) {
					str += 'a'+ i*4+j;
				}
			}
		}
		ans ++;
//		cout << str << endl;
		s.insert( str );
	} else {
		for ( int i = 0 ; i < 4 ; ++ i ) {
			int nx = x+dx[i];
			int ny = y+dy[i];
			if ( valid(nx,ny) && vis[nx][ny] == 0 ) {
				vis[nx][ny] = 1;
				dfs( nx, ny, step+1 );
				vis[nx][ny] = 0;
			}
		}
	}
}

int main()
{
	std::ios::sync_with_stdio(false);
	
	for ( int i = 0 ; i < 3 ; ++ i ) {
		for ( int j = 0 ; j < 4 ; ++ j ) {
			vis[i][j] = 1;
			dfs(i,j,1);
			vis[i][j] = 0;
		}
	}
	// 深搜结果不对  因为有些情况搜索不到
	// - - o - 	或者  o o o o  
	// o o o -        - o - -
	// - o - -        - - - - 
	// 圆圈为邮票  -为空白 
	cout << s.size() << endl;
	return 0;
}

下面是正确解法。

#include <iostream>
#include <cstring>

using namespace std;

int a, b, c, d, e; 
int arr[15]; 
int dx[] = { -5, -1, 1, 5 };

bool valid( int x ) {
	return x>=1 && x<=14 && x!=5 && x!=10;
}

bool zero() {
	for ( int i = 1 ; i < 15 ; ++ i ) {
		if ( arr[i] ) return false;
	}
	return true;
}

void dfs( int x ) {
	if (zero()) {
		return ;
	} else {
		for( int i = 0 ; i < 4 ; ++ i ) {
			int nx = x+dx[i];
			if ( valid(nx) && arr[nx] ) {
				arr[nx] = 0;
				dfs( nx );
			}
		}
	}
}

// 从一个点深搜 能到达的点都置0  如果最后数组为全0 即说明胡连 
// 左右相邻为   +1  -1
// 上下相邻为   +5  -5 
bool ok() {
	memset( arr, 0, sizeof(arr) );
	arr[b] = arr[c] = arr[d] = arr[e] = 1;
	dfs(a);
	if ( zero() ) return true;
	return false;
} 

/* 
	+---+---+---+---+
	| 1 | 2 | 3 | 4 |
	+---------------+
	| 6 | 7 | 8 | 9 |
	+---------------+
	| 11| 12| 13| 14|
	+---------------+
*/

int main()
{
	int ans = 0;
	for ( a = 1 ; a <= 14 ; ++ a ) {
		if ( a == 5 || a == 10 ) continue;
		for ( b = a+1 ; b <= 14 ; ++ b ) {
			if ( b == 5 || b == 10 ) continue;
			for ( c = b+1 ; c <= 14 ; ++ c ) {
				if ( c == 5 || c == 10 ) continue;
				for ( d = c+1 ; d <= 14 ; ++ d ) {
					if ( d == 5 || d == 10 ) continue;
					for ( e = d+1 ; e <= 14 ; ++ e ) {
						if ( e == 5 || e == 10 ) continue;
						if ( ok() ) {
//							cout << a <<" " << b << " " << c << " " << d << " " << e << endl; 
							ans ++;
						}
					}
				}
			}
		}
	}
	cout << ans << endl;
	return 0;
}

第八题 :四平方和

题目叙述: 
四平方和定理,又称为拉格朗日定理: 
每个正整数都可以表示为至多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个非负整数,按从小到大排序,中间用空格分开

例如,输入: 

则程序应该输出: 
0 0 1 2

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

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

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

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

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

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

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

这题本来想 用a+b+c+d = x;然后预处理出两个数的和;则问题变成A+B = x;

然后枚举 A,即可二分查找 x-A是否存在。当存在时,使用一个简单的hash就可以还原出原数字。

我处理hash的方法是:map[val] = i*10000+j;因为通过计算可知sqrt(5000000)=2237。

都是四位数字,可以使用第四位保存一个数,高位保存一个数。最后即可还原出平方和公式。

但是在处理两个数的和的时候 是一个2237*2237的循环,还要保存下来,然后还要处理重复,排序。

我试过了用set,vector,数组,效率都很低,不知道是不是和电脑当时的环境有关,最后放弃了这种做法。

选择了直接暴力枚举,简单粗暴,还比较有效。

#include <iostream>
using namespace std;
int main()
{
	int n;
	int cnta, cntb, cntc, cntd;
	cin>>n;
	for(int a=0;a<=2237;a++) {
		cnta = a*a;
		if(cnta>n) continue;
		for(int b=a;b<=2237;b++) {
			cntb = b*b;
			if(cnta+cntb>n) break;
			for(int c=b;c<=2237;c++) {
				cntc = c*c;
				if(cnta+cntb+cntc>n) break;
				for(int d=c;d<=2237;d++) {
					cntd = d*d;
					if(cnta+cntb+cntc+cntd > n) break;
					if(cnta+cntb+cntc+cntd==n) {
						cout<<a<<" "<<b<<" "<<c<<" "<<d<<endl;
						return 0;
					}
				}
			}
		}
	}      
	return 0;	
}


第九题 :交换瓶子

题目叙述: 
有N个瓶子,编号 1 ~ N,放在架子上。

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

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

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

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

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

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

例如,输入: 

3 1 2 5 4

程序应该输出: 
3

再例如,输入: 

5 4 3 2 1

程序应该输出: 
2

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

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

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

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

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

简单的贪心,不在自己该在的位置,就和那个位置换一下。

#include <iostream>
#include <cstdio>

using namespace std;

int a[10010];

int main()
{
	int n;
	scanf( "%d", &n );
	for ( int i = 1 ; i <= n ; ++ i ) {
		scanf( "%d", &a[i] );
	}
	int ans = 0;
	for ( int i = 1 ; i <= n ; ) {
		if ( a[i] != i ) {
			swap( a[i], a[a[i]] );
			ans ++;
		} else {
			i ++;
		}
	}
	printf( "%d\n", ans );
	return 0;
}


第十题 :最大比例

题目叙述: 
X星球的某个大奖赛设了M级奖励。每个级别的奖金是一个正整数。 
并且,相邻的两个级别间的比例是个固定值。 
也就是说:所有级别的奖金数构成了一个等比数列。比如: 
16,24,36,54 
其等比值为:3/2

现在,我们随机调查了一些获奖者的奖金数。 
请你据此推算可能的最大的等比值。

输入格式: 
第一行为数字N,表示接下的一行包含N个正整数 
第二行N个正整数Xi(Xi<1 000 000 000 000),用空格分开。每个整数表示调查到的某人的奖金数额

要求输出: 
一个形如A/B的分数,要求A、B互质。表示可能的最大比例系数

测试数据保证了输入格式正确,并且最大比例是存在的。

例如,输入: 

1250 200 32

程序应该输出: 
25/4

再例如,输入: 

3125 32 32 200

程序应该输出: 
5/2

再例如,输入: 

549755813888 524288 2

程序应该输出: 
4/1

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

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

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

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

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







猜你喜欢

转载自blog.csdn.net/hopygreat/article/details/79713540