2017-第八届蓝桥杯大赛个人赛省赛(软件类)真题 C大学A组
题目一览:
1.迷宫
2.跳蚱蜢
3.魔方状态
4.方格分割
5.字母组串
6.最大公共子串
7.正则问题
8.包子凑数
9.分巧克力
10.油漆面积
1.迷宫
X星球的一处迷宫游乐场建在某个小山坡上。
它是由10x10相互连通的小房间组成的。
房间的地板上写着一个很大的字母。
我们假设玩家是面朝上坡的方向站立,则:
L表示走到左边的房间,
R表示走到右边的房间,
U表示走到上坡方向的房间,
D表示走到下坡方向的房间。
X星球的居民有点懒,不愿意费力思考。
他们更喜欢玩运气类的游戏。这个游戏也是如此!
开始的时候,直升机把100名玩家放入一个个小房间内。
玩家一定要按照地上的字母移动。
迷宫地图如下:
------------
UDDLUULRUL
UURLLLRRRU
RRUURLDLRD
RUDDDDUUUU
URUDLLRRUU
DURLRLDLRL
ULLURLLRDU
RDLULLRDDD
UUDDUDUDLL
ULRDLUURRR
------------
请你计算一下,最后,有多少玩家会走出迷宫?
而不是在里边兜圈子。
请提交该整数,表示走出迷宫的玩家数目,不要填写任何多余的内容。
如果你还没明白游戏规则,可以参看一个简化的4x4迷宫的解说图:
p1.png
思路:模拟即可。走出判定:出界;走不出判定:走到了原来走过的点。
1 #include <bits/stdc++.h> 2 using namespace std; 3 4 string s[101]; 5 bool flag, vis[11][11], Break; 6 int Ans; 7 8 void dfs(int x, int y) { 9 if(x<0 || x>9 || y<0 || y>9) { // 走出 10 flag = true; 11 return ; 12 } 13 if(vis[x][y]) { // 走到了走过的点 14 Break = true; 15 return ; 16 } 17 if(Break || flag) return; 18 vis[x][y] = true; // 标记 19 if(s[x][y] == 'U') x--; // 下一步 20 else if(s[x][y] == 'D') x++; 21 else if(s[x][y] == 'L') y--; 22 else if(s[x][y] == 'R') y++; 23 dfs(x, y); 24 } 25 26 int main() { 27 for(int i=0; i<10; ++i) 28 cin >> s[i]; 29 for(int i=0; i<10; ++i) { 30 for(int j=0; j<10; ++j) { 31 flag = Break = false;// 每次都要初始化 32 memset(vis, false, sizeof(vis)); 33 dfs(i, j); 34 if(flag) Ans++; 35 } 36 } 37 printf("%d\n", Ans); 38 return 0; 39 }
答案:31
2.跳蚱蜢
如图 p1.png 所示:
有9只盘子,排成1个圆圈。
其中8只盘子内装着8只蚱蜢,有一个是空盘。
我们把这些蚱蜢顺时针编号为 1~8
每只蚱蜢都可以跳到相邻的空盘中,
也可以再用点力,越过一个相邻的蚱蜢跳到空盘中。
请你计算一下,如果要使得蚱蜢们的队形改为按照逆时针排列,
并且保持空盘的位置不变(也就是1-8换位,2-7换位,...),至少要经过多少次跳跃?
注意:要求提交的是一个整数,请不要填写任何多余内容或说明文
思路:宽搜,每次将四种跳法后的局面判重加入即可。
1 #include <bits/stdc++.h> 2 #include <queue> 3 using namespace std; 4 5 char ss[1010][1010]; 6 int len; 7 set<string> vis; 8 9 struct Node { 10 string s; // 当前局面 11 int pos, cnt; // 空格位置 到达该局面的步数 12 }; 13 14 void bfs() { 15 queue<Node> q; 16 Node head; 17 head.pos = head.cnt = 0; head.s = "012345678"; // 初始局面 18 q.push(head); 19 while(!q.empty()) { 20 head = q.front(); q.pop(); 21 if(head.s == "087654321") { // 到达目标局面 22 printf("Ans = %d\n", head.cnt); 23 return ; 24 } 25 for(int i=-2; i<=2; ++i) { // 四种跳法 26 if(i == 0) continue; 27 string str = head.s; 28 int tmp = head.pos; 29 swap(str[tmp], str[(tmp+i+9)%9]); 30 Node tail; 31 if(vis.count(str) == 0) { // 当前局面之前没有 32 vis.insert(str); 33 tail.s = str; 34 tail.pos = (tmp+i+9)%9; 35 tail.cnt = head.cnt+1; 36 q.push(tail); 37 } 38 } 39 } 40 } 41 42 int main() { 43 bfs(); 44 return 0; 45 }
答案:20
3.魔方状态
二阶魔方就是只有2层的魔方,只由8个小块组成。
如图p1.png所示。
小明很淘气,他只喜欢3种颜色,所有把家里的二阶魔方重新涂了颜色,如下:
前面:橙色
右面:绿色
上面:黄色
左面:绿色
下面:橙色
后面:黄色
请你计算一下,这样的魔方被打乱后,一共有多少种不同的状态。
如果两个状态经过魔方的整体旋转后,各个面的颜色都一致,则认为是同一状态。
请提交表示状态数的整数,不要填写任何多余内容或说明文字。
答案:229878
4.方格分割
6x6的方格,沿着格子的边线剪开成两部分。
要求这两部分的形状完全相同。
如图:p1.png, p2.png, p3.png 就是可行的分割法。
试计算:
包括这3种分法在内,一共有多少种不同的分割方法。
注意:旋转对称的属于同一种分割法。
请提交该整数,不要填写任何多余的内容或说明文字。
思路:这道题很巧妙,不按照格子搜索,搜索点,从中心点开始深搜,同时标记对称的点,当一边搜完时就是一个可行的方案。最后记得/4.
1 #include <bits/stdc++.h> 2 using namespace std; 3 4 int Ans; 5 int u[4] = {-1, 0, 0, 1}, v[4] = {0, -1, 1, 0}; 6 bool vis[10][10]; 7 8 bool check(int x, int y) { 9 if(vis[x][y]) return false; // 走过了 10 if(x<0 || x>6) return false; // 出界 11 if(y<0 || y>6) return false; 12 return true; 13 } 14 15 void dfs(int x, int y) { 16 if(x==0 || x==6 || y==0 || y==6) { // 搜到边界了 17 Ans ++; 18 return ; 19 } 20 vis[x][y] = vis[6-x][6-y] = true; // 标记 21 for(int i=0; i<4; ++i) { // 四个方向 22 int xx = x + u[i]; 23 int yy = y + v[i]; 24 if(check(xx, yy)) { 25 dfs(xx, yy); 26 } 27 } 28 vis[x][y] = vis[6-x][6-y] = false; // 回溯 29 } 30 31 int main() { 32 memset(vis, false, sizeof(vis)); 33 dfs(3, 3); // 从中心点开始搜索 34 printf("%d\n", Ans/4); // 有重复 35 return 0; 36 }
答案:509
5.字母组串
由 A,B,C 这3个字母就可以组成许多串。
比如:"A","AB","ABC","ABA","AACBB" ....
现在,小明正在思考一个问题:
如果每个字母的个数有限定,能组成多少个已知长度的串呢?
他请好朋友来帮忙,很快得到了代码,
解决方案超级简单,然而最重要的部分却语焉不详。
请仔细分析源码,填写划线部分缺少的内容。
1 #include <stdio.h> 2 3 // a个A,b个B,c个C 字母,能组成多少个不同的长度为n的串。 4 int f(int a, int b, int c, int n) 5 { 6 if(a<0 || b<0 || c<0) return 0; 7 if(n==0) return 1; 8 9 return _________________; // 填空 10 } 11 12 int main() 13 { 14 printf("%d\n", f(1,1,1,2)); 15 printf("%d\n", f(1,2,3,3)); 16 return 0; 17 }
对于上面的测试数据,小明口算的结果应该是:
6
19
注意:只填写划线部分缺少的代码,不要提交任何多余内容或说明性文字。
思路:递归题,通过第7行知道n是目前需要填充的长度,那么我们每次填充一个A、B或者C,那么其个数需要减1,目前需要填充的长度也要减1.
答案:
f(a-1, b, c, n-1)+f(a, b-1, c, n-1)+f(a, b, c-1, n-1)
6.最大公共子串
最大公共子串长度问题就是:
求两个串的所有子串中能够匹配上的最大长度是多少。
比如:"abcdkkk" 和 "baabcdadabc",
可以找到的最长的公共子串是"abcd",所以最大公共子串长度为4。
下面的程序是采用矩阵法进行求解的,这对串的规模不大的情况还是比较有效的解法。
请分析该解法的思路,并补全划线部分缺失的代码。
1 #include <stdio.h> 2 #include <string.h> 3 4 #define N 256 5 int f(const char* s1, const char* s2) 6 { 7 int a[N][N]; 8 int len1 = strlen(s1); 9 int len2 = strlen(s2); 10 int i,j; 11 12 memset(a,0,sizeof(int)*N*N); 13 int max = 0; 14 for(i=1; i<=len1; i++){ 15 for(j=1; j<=len2; j++){ 16 if(s1[i-1]==s2[j-1]) { 17 a[i][j] = ______; //填空 18 if(a[i][j] > max) max = a[i][j]; 19 } 20 } 21 } 22 23 return max; 24 } 25 26 int main() 27 { 28 printf("%d\n", f("abcdkkk", "baabcdadabc")); 29 return 0; 30 }
注意:只提交缺少的代码,不要提交已有的代码和符号。也不要提交说明性文字。
思路:DP题,看第16行,当前两个字符相同,那么以他俩结尾的长度就是以前一个字符结尾的长度+1。针对最长公共子序列的问题可以看着。(待补)
答案:
a[i-1][j-1] + 1
7.正则问题
考虑一种简单的正则表达式:
只由 x ( ) | 组成的正则表达式。
小明想求出这个正则表达式能接受的最长字符串的长度。
例如 ((xx|xxx)x|(x|xx))xx 能接受的最长字符串是: xxxxxx,长度是6。
输入
----
一个由x()|组成的正则表达式。输入长度不超过100,保证合法。
输出
----
这个正则表达式能接受的最长字符串的长度。
例如,
输入:
((xx|xxx)x|(x|xx))xx
程序应该输出:
6
资源约定:
峰值内存消耗(含虚拟机) < 256M
CPU消耗 < 1000ms
请严格按要求输出,不要画蛇添足地打印类似:“请您输入...” 的多余内容。
注意:
main函数需要返回0;
只使用ANSI C/ANSI C++ 标准;
不要调用依赖于编译环境或操作系统的特殊函数。
所有依赖的函数必须明确地在源文件中 #include <xxx>
不能通过工程设置而省略常用头文件。
提交程序时,注意选择所期望的语言类型和编译器类型。
思路:首先理解样例的6是这么来的,|是取左右里较多的哪一个。((xx|xxx)x|(x|xx))xx,
第一步去括号里面左侧的括号:(xxx x|(x|xx))xx
第二步去括号里面右侧的括号:(xxx x| xx)xx
第三步去外层括号:xxx x xx
答案就是6.
代码的话就是递归,然后是四种情况。
1 #include <bits/stdc++.h> 2 using namespace std; 3 4 char s[105]; 5 int pos = 0, len; 6 7 int f() { 8 int Now_sum = 0, Max_sum = 0; 9 while(pos < len) { 10 if(s[pos] == '(') { // 左括号,位置加一 等待右括号 11 pos++; 12 Now_sum += f(); 13 } 14 else if(s[pos] == 'x') { // x 位置加一 计数器加一 15 pos ++; 16 Now_sum ++; 17 } 18 else if(s[pos] == ')') { // 左括号 位置加一 中断这次递归 19 pos ++; 20 break; 21 } 22 else if(s[pos] == '|') { // | 位置加一 23 pos++; 24 Max_sum = max(Max_sum, Now_sum); // 保留较大值 25 Now_sum = 0; //计数器清零 26 } 27 } 28 Max_sum = max(Max_sum, Now_sum); //保留较大值 29 return Max_sum; 30 } 31 32 int main() { 33 scanf("%s", s); 34 len = strlen(s); 35 int Ans = f(); 36 printf("%d\n", Ans); 37 return 0; 38 }
8.包子凑数
小明几乎每天早晨都会在一家包子铺吃早餐。他发现这家包子铺有N种蒸笼,其中第i种蒸笼恰好能放Ai个包子。每种蒸笼都有非常多笼,可以认为是无限笼。
每当有顾客想买X个包子,卖包子的大叔就会迅速选出若干笼包子来,使得这若干笼中恰好一共有X个包子。比如一共有3种蒸笼,分别能放3、4和5个包子。当顾客想买11个包子时,大叔就会选2笼3个的再加1笼5个的(也可能选出1笼3个的再加2笼4个的)。
当然有时包子大叔无论如何也凑不出顾客想买的数量。比如一共有3种蒸笼,分别能放4、5和6个包子。而顾客想买7个包子时,大叔就凑不出来了。
小明想知道一共有多少种数目是包子大叔凑不出来的。
输入
----
第一行包含一个整数N。(1 <= N <= 100)
以下N行每行包含一个整数Ai。(1 <= Ai <= 100)
输出
----
一个整数代表答案。如果凑不出的数目有无限多个,输出INF。
例如,
输入:
2
4
5
程序应该输出:
6
再例如,
输入:
2
4
6
程序应该输出:
INF
样例解释:
对于样例1,凑不出的数目包括:1, 2, 3, 6, 7, 11。
对于样例2,所有奇数都凑不出来,所以有无限多个。
资源约定:
峰值内存消耗(含虚拟机) < 256M
CPU消耗 < 1000ms
请严格按要求输出,不要画蛇添足地打印类似:“请您输入...” 的多余内容。
注意:
main函数需要返回0;
只使用ANSI C/ANSI C++ 标准;
不要调用依赖于编译环境或操作系统的特殊函数。
所有依赖的函数必须明确地在源文件中 #include <xxx>
不能通过工程设置而省略常用头文件。
提交程序时,注意选择所期望的语言类型和编译器类型。
1 #include <bits/stdc++.h> 2 using namespace std; 3 4 int n, a[1010], Ans, g; 5 bool f[10010]; 6 7 int gcd(int a, int b) { 8 return b? gcd(b, a%b):a; 9 } 10 11 int main() { 12 cin >> n; 13 memset(f, false, sizeof(f)); 14 f[0] = true; 15 for(int i=0; i<n; ++i) { 16 scanf("%d", &a[i]); 17 if(i == 0) g = a[i]; 18 else g = gcd(g, a[i]); 19 for(int j=0; j<10000; ++j) 20 if(f[j]) f[j+a[i]] = true; 21 } 22 if(g != 1) { 23 puts("INF"); 24 return 0; 25 } 26 bool flag = false; 27 int tot = 0; 28 for(int i=0; i<10000; ++i) { 29 if(!f[i]) Ans++; 30 } 31 printf("%d\n", Ans); 32 return 0; 33 }
9.分巧克力
儿童节那天有K位小朋友到小明家做客。小明拿出了珍藏的巧克力招待小朋友们。
小明一共有N块巧克力,其中第i块是Hi x Wi的方格组成的长方形。
为了公平起见,小明需要从这 N 块巧克力中切出K块巧克力分给小朋友们。切出的巧克力需要满足:
1. 形状是正方形,边长是整数
2. 大小相同
例如一块6x5的巧克力可以切出6块2x2的巧克力或者2块3x3的巧克力。
当然小朋友们都希望得到的巧克力尽可能大,你能帮小Hi计算出最大的边长是多少么?
输入
第一行包含两个整数N和K。(1 <= N, K <= 100000)
以下N行每行包含两个整数Hi和Wi。(1 <= Hi, Wi <= 100000)
输入保证每位小朋友至少能获得一块1x1的巧克力。
输出
输出切出的正方形巧克力最大可能的边长。
样例输入:
2 10
6 5
5 6
样例输出:
2
资源约定:
峰值内存消耗(含虚拟机) < 256M
CPU消耗 < 1000ms
请严格按要求输出,不要画蛇添足地打印类似:“请您输入...” 的多余内容。
注意:
main函数需要返回0;
只使用ANSI C/ANSI C++ 标准;
不要调用依赖于编译环境或操作系统的特殊函数。
所有依赖的函数必须明确地在源文件中 #include <xxx>
不能通过工程设置而省略常用头文件。
提交程序时,注意选择所期望的语言类型和编译器类型。
方法一:要求边长最大那么我们就从最大开始枚举边长,不断减小来找到满足条件的边长。
1 #include <bits/stdc++.h> 2 using namespace std; 3 4 int N, K; 5 6 struct QKL { 7 int h, w; 8 }qkl[100010]; 9 10 int main() { // 暴力 11 cin >> N >> K; 12 for(int i=0; i<N; ++i) 13 scanf("%d%d", &qkl[i].h, &qkl[i].w); 14 int Ans = 100000; 15 while(Ans >= 1) { // 枚举边长 16 int cnt = 0; // 当前边长下能切多少 17 for(int i=0; i<N; ++i) // N块巧克力 18 cnt += (qkl[i].h/Ans) * (qkl[i].w/Ans); 19 if(cnt >= K) { // 由于我们从大到小枚举,第一个满足的就是最大的 20 printf("%d\n", Ans); 21 return 0; 22 } 23 Ans --; 24 } 25 return 0; 26 }
方法二:优化方法一,我们使用二分法,每次二分出一个边长,不够分说明边长大了,我们就减小一点。分的多,我们看看能不能使边长大一点。
1 #include <bits/stdc++.h> 2 using namespace std; 3 4 int N, K; 5 6 struct QKL { 7 int h, w; 8 }qkl[100010]; 9 10 bool check(int x) { 11 int cnt = 0; 12 for(int i=0; i<N; ++i) 13 cnt += (qkl[i].h/x) * (qkl[i].w/x); 14 if(cnt >= K) return true; // 能分成K块 15 else return false; // 分不成K块 16 } 17 18 int main() { // 二分 19 cin >> N >> K; 20 for(int i=0; i<N; ++i) 21 scanf("%d%d", &qkl[i].h, &qkl[i].w); 22 int l = 0, r = 100001; 23 while(l <= r) { 24 int m = (l+r) / 2; 25 if(check(m)) l = m+1; // 边长可以再大一点 26 else r = m-1; // 边长大了,小一点 27 } 28 printf("%d\n", l-1); 29 return 0; 30 }
10.油漆面积
X星球的一批考古机器人正在一片废墟上考古。
该区域的地面坚硬如石、平整如镜。
管理人员为方便,建立了标准的直角坐标系。
每个机器人都各有特长、身怀绝技。它们感兴趣的内容也不相同。
经过各种测量,每个机器人都会报告一个或多个矩形区域,作为优先考古的区域。
矩形的表示格式为(x1,y1,x2,y2),代表矩形的两个对角点坐标。
为了醒目,总部要求对所有机器人选中的矩形区域涂黄色油漆。
小明并不需要当油漆工,只是他需要计算一下,一共要耗费多少油漆。
其实这也不难,只要算出所有矩形覆盖的区域一共有多大面积就可以了。
注意,各个矩形间可能重叠。
本题的输入为若干矩形,要求输出其覆盖的总面积。
输入格式:
第一行,一个整数n,表示有多少个矩形(1<=n<10000)
接下来的n行,每行有4个整数x1 y1 x2 y2,空格分开,表示矩形的两个对角顶点坐标。
(0<= x1,y1,x2,y2 <=10000)
输出格式:
一行一个整数,表示矩形覆盖的总面积。
例如,
输入:
3
1 5 10 10
3 1 20 20
2 7 15 17
程序应该输出:
340
再例如,
输入:
3
5 2 10 6
2 7 12 10
8 1 15 15
程序应该输出:
128
资源约定:
峰值内存消耗(含虚拟机) < 256M
CPU消耗 < 2000ms
请严格按要求输出,不要画蛇添足地打印类似:“请您输入...” 的多余内容。
注意:
main函数需要返回0;
只使用ANSI C/ANSI C++ 标准;
不要调用依赖于编译环境或操作系统的特殊函数。
所有依赖的函数必须明确地在源文件中 #include <xxx>
不能通过工程设置而省略常用头文件。
提交程序时,注意选择所期望的语言类型和编译器类型。
方法一:直接模拟,设置一个二维数组初始化为false,需要涂油漆的地方改为true,最后统计true的个数即可。由于官方数据较水,该方法不会超时。
1 #include <bits/stdc++.h> 2 using namespace std; 3 4 int N, Ans = 0; 5 bool vis[10006][10006]; 6 7 void work(int x, int y, int xx, int yy) { 8 for(int i=x; i<xx; ++i) 9 for(int j=y; j<yy; ++j) 10 vis[i][j] = true; 11 } 12 13 int main() { 14 cin >> N; 15 memset(vis, false, sizeof(vis)); 16 for(int i=1; i<=N; ++i) { 17 int x, y, xx, yy; 18 scanf("%d%d%d%d", &x, &y, &xx, &yy); 19 work(x, y, xx, yy); 20 } 21 for(int i=0; i<10005; ++i) 22 for(int j=0; j<10005; ++j) 23 if(vis[i][j]) { 24 //printf("%d %d\n", i, j); 25 Ans++; 26 } 27 printf("%d\n", Ans); 28 return 0; 29 }
方法二:正解解法:线段树+扫描线。
先挖个坑
PS:官方有个数据是错的,正确答案是4909,而给的答案是3796.想AC的话可以特判一下。面向数据编程
posted on 2020-04-06 13:28 Marginalin 阅读(2688) 评论(0) 编辑 收藏 举报