2016第七届蓝桥杯CC++B组题解

    xiaoxiao2021-03-25  70

    声明:本文为转载,觉得这位大牛的解题思路巧妙地用到了很多算法,真心觉得好厉害

    第一题: 煤球数目 有一堆煤球,堆成三角棱锥形。具体: 第一层放1个, 第二层3个(排列成三角形), 第三层6个(排列成三角形), 第四层10个(排列成三角形), .... 如果一共有100层,共有多少个煤球? 请填表示煤球总数目的数字。 注意:你提交的应该是一个整数,不要填写任何多余的内容或说明性文字。

    这道题坑死了,第一次看堆成三角棱锥形,草稿本画半天都没画出个三角棱锥。后来单独看每句话才知道每层一个三角形叠起来就是三角棱锥。我去。

    看懂题目这个题目就很简单了,每层的个数是上层的个数加上层数,意思就是An = An-1 + n,然而题目是求的前100层一共多少煤球。所以是Sn.代码双重for循环就出来了。答案是:171700

    #include<stdio.h> int main() { int a[101] ={0}; for(int i = 1 ; i < 101 ; i ++) a[i] = a[i-1] + i; int ans = 0; for(int j = 1 ; j < 101 ; j ++) ans += a[j]; printf("%d\n",ans); return 0; }

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

    呵呵,水题,但是出题人不严谨啊!!!怎么就不能考虑万一他今年236岁呢....好了不说了强迫症犯了。

    蓝桥杯这种不像acm的题目的,能暴力直接暴力。不用想太多。直接从1~236 枚举 start, end 分别表示他开始过生日的年龄和今年的年龄,然后计算之间吹蜡烛的总和如果等于236就输出start ,end.  答案是:26

    #include<stdio.h> int main() { int start,end; for(start = 1 ; start < 236 ; start ++) { for( end = start ; end < 236 ; end ++ ) { int sum = 0; for(int i = start; i <= end; i ++) sum += i; if( sum == 236) { printf("start : %d end : %d\n",start,end); } } } return 0; }

    第三题: 凑算式        B      DEF A + — + -——— = 10        C       GHI (如果显示有问题,可以参见【图1.jpg】) 这个算式中A~I代表1~9的数字,不同的字母代表不同的数字。 比如: 6+8/3+952/714 就是一种解法, 5+3/1+972/486 是另一种解法。 这个算式一共有多少种解法? 注意:你提交应该是个整数,不要填写任何多余的内容或说明性文字。

    这个题不多说了,直接暴力生成9的全排列然后去验证等式是否成立,只是验证的时候如果防止精度问题可以通分把除法变成乘法。 答案是:29 #include<stdio.h> int ans = 0; int num[10]; bool visit[10]; void Solve() { double sum = num[0] + (double)num[1] / num[2] + (double)(num[3]*100+num[4]*10+num[5])/(num[6]*100+num[7]*10+num[8]); if(sum == 10) { ans ++; } } void dfs(int index) { if(index == 9) { Solve(); return ; } for(int i = 1 ; i < 10 ; i ++) { if(!visit[i]) { visit[i] = true; num[index] = i; dfs(index+1); visit[i] = false; } } } int main() { dfs(0); printf("%d\n",ans); 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).

    注意:我开始这题一直看不懂,然后就上网搜了一下快排,在我的博客里有一篇快排原理,真心觉得不错,主要是配上图很快就理解了。

              链接给一下: http://blog.csdn.net/danting1126/article/details/61627737

    第五题: 抽签 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(int a[],int k,int m,char b[]).a[] 是每个国家的最多指派人数,k表示当前是哪个国家,m表示还需要派送几个人(可以为负数).b表示已经派送的人的字符串。 所以这个题目在递归中间的的 第一个循环表示从0~a[i]中让i国选择指派人数,内循环只是向b[]记录的过程。 所以答案是f(a,k+1,m-i,b).     因为这里I = j .应该 f(a,k+1,m-j,b)也可以。 第六题: 方格填数 如下的10个格子 (如果显示有问题,也可以参看【图1.jpg】) 填入0~9的数字。要求:连续的两个数字不能相邻。 (左右、上下、对角都算相邻) 一共有多少种可能的填数方案? 请填写表示方案数目的整数。 注意:你提交的应该是一个整数,不要填写任何多余的内容或说明性文字。 这个题目题目有点表述不明,不知道0~9 可不可以重复使用。我当时做的时候是当作不可以重复使用来处理的。那么这里我就先当作不可重复使用来讲解。 这里题目还是一样先往里面填数。用生成排列的形式。填写完了之后再判断是否可行。答案是: 1580 #include <stdio.h> #include <math.h> int flag[3][4]; //表示哪些可以填数 int mpt[3][4]; //填数 bool visit[10]; int ans = 0; void init() //初始化 { int i,j; for(i = 0 ; i < 3 ; i ++) for(j = 0 ; j < 4 ; j ++) flag[i][j] = 1; flag[0][0] = 0; flag[2][3] = 0; } void Solve() { int dir[8][2] = { 0,1,0,-1,1,0,-1,0,1,1,1,-1,-1,1,-1,-1}; int book = true; for(int i = 0 ; i < 3 ; i ++) { for(int j = 0 ; j < 4; j ++) { //判断每个数周围是否满足 if(flag[i][j] == 0)continue; for( int k = 0 ; k < 8 ; k ++) { int x,y; x = i + dir[k][0]; y = j + dir[k][1]; if(x < 0 || x >= 3 || y < 0 || y >= 4 || flag[x][y] == 0) continue; if(abs(mpt[x][y] - mpt[i][j]) == 1) book = false; } } } if(book) ans ++; } void dfs(int index) { int x,y; x = index / 4; y = index % 4; if( x == 3) { Solve(); return; } if(flag[x][y]) { for(int i = 0 ; i < 10 ; i ++) { if(!visit[i]) { visit[i] = true; mpt[x][y] = i; dfs(index+1); visit[i] = false; } } } else { dfs(index+1); } } int main() { init(); dfs(0); printf("%d\n",ans); return 0; } 第七题: 剪邮票 如【图1.jpg】, 有12张连在一起的12生肖的邮票。 现在你要从中剪下5张来,要求必须是连着的。 (仅仅连接一个角不算相连) 比如,【图2.jpg】,【图3.jpg】中,粉红色所示部分就是合格的剪取。 请你计算,一共有多少种不同的剪取方法。 请填写表示方案数目的整数。 注意:你提交的应该是一个整数,不要填写任何多余的内容或说明性文字。 其实这个题目还是可前面的一样,先生成,再判断是否可行。这里我们可以先用搜索从12个数里面将所有5个数的组合找出来。然后再用深搜判断这五个是否连在一起。答案是: 116 #include <stdio.h> #include <string.h> int mpt[3][4]; int mpt_visit[3][4]; int num[6]; int have[13]; int visit[13]; int ans = 0; int Count = 0; void init() { int k = 1; for(int i = 0 ; i < 3 ; i ++) for(int j = 0 ; j < 4 ; j ++) { mpt[i][j] = k; k ++; } } int dir[4][2] = {0,1,0,-1,-1,0,1,0}; //判断五个数是否能连在一起 void dfs_find(int x,int y) { for(int i = 0 ; i < 4 ; i++) { int tx,ty; tx = x + dir[i][0]; ty = y + dir[i][1]; if(tx < 0 || tx >= 3 || ty < 0 || ty >= 4) continue; if(have[mpt[tx][ty]] == 0 || mpt_visit[tx][ty])continue; mpt_visit[tx][ty] = 1; Count ++; dfs_find(tx,ty); } } void Solve() { int i; memset(have,0,sizeof(have)); memset(mpt_visit,0,sizeof(mpt_visit)); for(i = 1; i < 6 ; i ++) have[num[i]] = 1; for(i = 0 ; i < 12 ; i ++) { int x,y; x = i / 4; y = i % 4; if(have[mpt[x][y]]) { Count = 1; mpt_visit[x][y] =1; dfs_find(x,y); break; } } if(Count == 5) { ans ++; } } //创建5个数的组合 void dfs_creat(int index) { if(index == 6) { Solve(); return; } for(int i = num[index-1] + 1; i < 13 ; i ++) { if(!visit[i]) { visit[i] = true; num[index] = i; dfs_creat(index+1); visit[i] = false; } } } int main() { init(); dfs_creat(1); printf("%d\n",ans); 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个非负整数,按从小到大排序,中间用空格分开 例如,输入: 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 <xxx>, 不能通过工程设置而省略常用头文件。 提交时,注意选择所期望的编译器类型。 这个题目很水也是搜索能做的。但是有点技巧,这里我贡献两个方法给大家参考。 方法一:O(n^3/2).先暴力枚举前三个数然后做减法判断差是否为一个完全平方数即可。当然虽然这个题目是n^3/2看数据貌似过不了。但是貌似我找了几组数据都能秒出结果。应该是绝大多数最外层循环都不会太多。 #include <stdio.h> #include <math.h> int main() { int n; int flag = false; scanf("%d",&n); for(int i = 0 ; i * i <= n ; i ++) { for(int j = 0 ; j * j <= n ; j ++){ for(int k = 0 ; k * k <= n ; k ++) { int temp = n - i*i - j*j - k*k; double l = sqrt((double) temp); if(l == (int)l ) { printf("%d %d %d %d\n",i,j,k,(int)l); flag = true; break; } } if(flag)break; } if(flag)break; } return 0; } 方法二:O(n) 的方法。这个方法是我在比赛的时候用的。先把两个平方数能相加的到的数字球出来然后记录。这样我们第三层循环就可以先判断再循环了。 #include <stdio.h> #include <math.h> int mpt[5000010] ={0}; //mpt[i] = 1表示i 能够用两个完全平方数相加而得。 int n; void init() { for(int i = 0 ; i*i <= n ; i ++) for(int j = 0 ; j*j <=n ; j ++) if(i*i+j*j <= n) mpt[i*i+j*j] = 1; } int main() { int flag = false; scanf("%d",&n); init(); for(int i = 0 ; i * i <= n ; i ++) { for(int j = 0 ; j * j <= n ; j ++){ if(mpt[n - i*i - j*j] == 0) continue; //如果剩下的差用两个完全平方数不能组合出来就不继续 for(int k = 0 ; k * k <= n ; k ++) { int temp = n - i*i - j*j - k*k; double l = sqrt((double) temp); if(l == (int)l ) { printf("%d %d %d %d\n",i,j,k,(int)l); flag = true; break; } } if(flag)break; } if(flag)break; } return 0; } 第九题: 交换瓶子 有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 <xxx>, 不能通过工程设置而省略常用头文件。 提交时,注意选择所期望的编译器类型。 啊啊这个题目最开始没看到最上面的那句话,以为输入的数字是随意的int.还做了离散化处理。后来看到了编号为1~n就觉得自己傻逼了。 这个题目我用的贪心,从左向右如果当前第i个瓶子编号不是i就把编号为i的瓶子换过来。自己yy几发数据貌似都能过。不清楚是否对。 因为这里的n规模为10000,如果在交换的时候去用for找编号为i的瓶子在哪儿时间复杂度为O(n^2)会超时。 所以这里要用两个数组,一个数组是记录第i个瓶子编号为多少,一个是记录编号为i的瓶子在哪儿。注意我们在交换的时候要把这两个数组都维护! #include <stdio.h> #include <math.h> int arr[10010]; int flag[10010]; int main() { int ans = 0; int n,i; scanf("%d",&n); for(i = 1 ; i <= n ; i ++) scanf("%d",&arr[i]); for(i = 1 ; i <= n ; i ++ )flag[arr[i]] = i; for(i = 1 ; i <= n ; i ++) { if( i != arr[i] ) { int x = arr[i]; arr[i] ^= arr[flag[i]] ^= arr[i] ^= arr[flag[i]]; flag[i] ^= flag[x] ^= flag[i] ^= flag[x]; ans ++; } } 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互质。表示可能的最大比例系数 测试数据保证了输入格式正确,并且最大比例是存在的。 例如,输入: 3 1250 200 32 程序应该输出: 25/4 再例如,输入: 4 3125 32 32 200 程序应该输出: 5/2 再例如,输入: 3 549755813888 524288 2 程序应该输出: 4/1 资源约定: 峰值内存消耗 < 256M CPU消耗 < 3000ms 请严格按要求输出,不要画蛇添足地打印类似:“请您输入...” 的多余内容。 所有代码放在同一个源文件中,调试通过后,拷贝提交该源码。 注意: main函数需要返回0 注意: 只使用ANSI C/ANSI C++ 标准,不要调用依赖于编译环境或操作系统的特殊函数。 注意: 所有依赖的函数必须明确地在源文件中 #include <xxx>, 不能通过工程设置而省略常用头文件。 提交时,注意选择所期望的编译器类型。 这个题目刚开始没准备做后来感觉时间蛮充裕的(上次吃了时间的亏这次自己吧时间卡的比较死,一个小时做完了填空题,一个半小时做完了前面两道大题。所以还剩下一个半小时.)想了一会儿感觉这个题可做。自己YY了一下。不保证一定正确,可以当作一个参考!

    这个题目意思是相邻两个等级的奖金比例一定是一个相同的分数P(P = A/B, A/B为最简分数).题目告诉了你n个人分别领取了多少奖金。让你算这个P最大为多少。

    我是这么处理的,我们先把n个人的奖金从大到小排序。剔除掉相同的(相同的奖金只保留一个)。然后前后做除用分数的形式保留前后两个数的比例。如何用分数表示两个数的最简分数呢。例如 A / B = (A/Gcd(A,B)) / (B/Gcd(A,B))。只要把A,B最大公约数去除掉就可以了。

    现在得到了一堆堆分数Fs[i]然后怎么处理呢?我们知道 这些分数一定都满足一个条件就是  Fs[i] = P^ki  这里的ki是不定的。但是要使得 求得P最大,那么一定是 所有 ki 求 最大公约数 得到k' .结果就是 P*k'. 然而这道题数据太大了,并不容易求出这些分数对应的底数以及各自的指数。那么这个方案就不可取了。所以我们要的到结果并不能先求出k‘.但是我们看我们能不能直接求出P^k'.  这里我们先把分子分母分开处理。因为A/B是最简分数。 P^k' = A^k' / B^k'.

    我们先求A^k' . 

    不知道大家还记不记得欧几里得游戏。这个游戏是这样的。黑板上有两个不同的正整数n,m。现在两个人轮流在黑板上写一个正整数要求这个正整数必须为黑板上某两个数的差并且没有在黑板上出现过。当某人无法再写下一个数的时候游戏结束。其实最后这个游戏本质就是看黑板上最多能写多少个数。事实上我们知道黑板上能写的数都是最开始两个数的最大公约数的倍数。所以黑板上最小的数也就是m,n的最大公约数。

    这里我们来看,假如我们先求A^k1,A^k2 指数对应的最大公约数。我们吧这个两个数做除法。发现为 A^(k1-k2)。发现是不是这个题目就转化为欧几里得游戏的填数游戏了。只不过加法改成了除法。所以当出发找到一个没有出现过的商的时候这些数种最小的就是当前的A^k'.然后依次对剩下的A^ki做相同操作就可以得到最终的A^ki,同样可以得到B^ki.最后输出 A^ki/B^ki即可。

    [cpp]  view plain  copy   #include <stdio.h>   #include <algorithm>   #include <queue>   using namespace std;   #define LL long long   struct fs   {       LL up,down;   };   int n;   LL arr[110];   fs Fs[110];      bool cmp(LL a,LL b)   {       return a > b;   }      LL Gcd(LL a,LL b)   {       if( b == 0 )return a;       return Gcd(b,a%b);   }   LL Get(LL a, LL b)   {       if( a < b) a ^= b ^= a ^= b;       LL v[30];       queue<LL>team;       if( a == b || a / b == a) return b;       v[0] = a, v[1] = b;       v[2] = a / b;       int top = 3,i,j;       team.push(a/b);       while(team.size())       {           LL now = team.front();           team.pop();           for(i = 0 ; i < top ; i ++)           {               LL temp = (v[i] > now) ? v[i] / now : now / v[i];               bool find = false;               for(j = 0 ; j < top ; j ++)                   if( v[j] == temp) find = true;               if(find == truecontinue;               team.push(temp);               v[top++] = temp;           }       }       LL ans = v[0];       for(i = 0 ; i < top ; i ++)            if(v[i] != 1)            {               ans = v[i];               break;           }       for(i = 0 ; i < top ; i ++)           if( v[i] < ans && v[i] != 1) ans = v[i];       return ans;   }   int main()   {       int i,j;       scanf("%d",&n);       for(i = 0 ; i < n ; i ++) scanf("%lld",&arr[i]);       sort(arr,arr+n,cmp);       int top = 1;       for(i = 1; i < n ; i ++)           if(arr[i] != arr[i-1]) arr[top++] = arr[i];       n = top;       for(i = 0 ; i < n - 1; i ++)       {           LL gcd = Gcd(arr[i],arr[i+1]);           Fs[i].up = arr[i] / gcd;           Fs[i].down = arr[i+1] / gcd;       }       LL x = Fs[0].up;       for(i = 0 ; i < n - 1 ; i ++)           x = Get(x,Fs[i].up);       LL y = Fs[0].down;       for(i = 0 ; i < n - 1; i ++)           y = Get(y,Fs[i].down);       printf("%lld/%lld\n",x,y);       return 0;   }  
    转载请注明原文地址: https://ju.6miu.com/read-37413.html

    最新回复(0)