距离蓝桥杯省赛还有1个多月,为了拿到更好地成绩,让我们刷起来。
一:填空题
1. ASC
已知大写字母 A 的 ASCII 码为 65,请问大写字母 L 的 ASCII 码是多少?
分析:签到题。看到这题的时候会想,这题咋这么简单,直接计算就行,都不需要写程序。实际上,在蓝桥杯省赛中,填空题的前两题和编程题的第一题都是签到题,比较简单。但同时,这也是我们要想尽办法拿下来的题目,因此一定要细心,细心,再细心。同时,我建议简单的题也最好是编程解决或者用计算器计算,毕竟考试的时候紧张的话还是有可能算错最基本的运算的。
import java.util.Scanner;// 1:无需package// 2: 类名必须Main, 不可修改public class Main {public static void main(String[] args) {System.out.println((int)'L');}}
输出答案:
76
注意:填空题只需要填最后的答案即可,因此可采取所有得到答案的方法。暴力+合理利用工具。
2. 卡片
小蓝有很多数字卡片,每张卡片上都是数字 0 到 9。 小蓝准备用这些卡片来拼一些数,他想从 1 开始拼出正整数,每拼一个, 就保存起来,卡片就不能用来拼其它数了。 小蓝想知道自己能从 1 拼到多少。 例如,当小蓝有 30张卡片,其中 0 到 9 各 3 张,则小蓝可以拼出 1 到 10, 但是拼 11 时卡片 1 已经只有一张了,不够拼出 11。 现在小蓝手里有 0到 9的卡片各 2021 张,共 20210 张,请问小蓝可以从 1拼到多少? 提示:建议使用计算机编程解决问题
分析:从1开始遍历,判断遍历到的数可否用目前的卡片拼出来,可以的话,继续遍历,否则输出答案(答案为现在遍历到的数减1)。
import java.util.*;// 1:无需package// 2: 类名必须Main, 不可修改public class Main {public static void main(String[] args) {Scanner scan = new Scanner(System.in);//在此输入您的代码...int[] arr = new int[10];Arrays.fill(arr, 2021);for(int i = 1;;i++){int temp = i;while(temp > 0){int r = temp % 10;if(arr[r] > 0){arr[r]--;}else{break;}temp /= 10;}if(temp > 0){System.out.println(i - 1);break;}}scan.close();}}
输出答案:
3181
对于这道题,如果想要验证自己得到的答案是否正确的话。可以修改一下程序,主动输入一个n,n表示有0到9的卡片各n张,然后输入3,如果输出的是10,则意味着自己的计算是正确的。
3. 直线
在平面直角坐标系中,两点可以确定一条直线。如果有多点在一条直线上,
那么这些点中任意两点确定的直线是同一条。
给定平面上 2 × 3 个整点 {(x,y)|0 ≤ x < 2,0 ≤ y < 3, x ∈ Z,y ∈ Z},即横坐标
是 0 到 1 (包含 0 和 1) 之间的整数、纵坐标是 0 到 2 (包含 0 和 2) 之间的整数
的点。这些点一共确定了 11 条不同的直线。
给定平面上 20 × 21 个整点 {(x,y)|0 ≤ x < 20,0 ≤ y < 21, x ∈ Z,y ∈ Z},即横
坐标是 0 到 19 (包含 0 和 19) 之间的整数、纵坐标是 0 到 20 (包含 0 和 20) 之
间的整数的点。请问这些点一共确定了多少条不同的直线。
分析:枚举两个不同的点,两点确定一条直线。具体的,直线由y=kx+b表示,看有多少种(k,b)的组合。但由于k和b都是浮点数,Java中是不能够通过==直接判断两个浮点数是否相等的,为此我们用”(b2 – b1) / (a2 – a1) (b1 * (a2 – a1) – a1 * (b2 – b1) / (a2 – a1))”字符串的形式表示一根直线。然后通过Set集合去重,自定义的类需要通过重写equals方法和hashCode()方法才能被Set集合去重。
import java.util.*;public class Main {public static void main(String[] args) {Set<String> ans = new HashSet<String>();for(int a1 = 0; a1 <= 19; a1++) {for(int b1 = 0; b1 <= 20; b1++) {for(int a2 = 0; a2 <= 19; a2++) {for(int b2 = 0; b2 <= 20; b2++) {// 斜率不存在和斜率为0的特殊情况,我们可以手动计算无需特殊判断if(a1 == a2 || b1 == b2) {continue;}// 以分子/分母的形式表达斜率k和截距b时,分子和分母需要是最简的形式StringBuilder sb = new StringBuilder();int up = b2 - b1;int down = a2 - a1;int r = gcd(up, down);sb.append(up / r + " ");sb.append(down / r + " ");up = b1 * down - a1 * up;r = gcd(up, down);sb.append(up / r + " ");sb.append(down / r);ans.add(sb.toString());}}}}// 斜率不存在的直线20根,斜率为0的直线21根System.out.println(ans.size() + 20 + 21);}static int gcd(int a, int b) {return b == 0 ? a : gcd(b, a % b);}}
输出:
40257
4. 货物摆放
现在,小蓝有 n箱货物要摆放在仓库,每箱货物都是规则的正方体。小蓝规定了长、宽、高三个互相垂直的方向,每箱货物的边都必须严格平行于长、宽、高。
小蓝希望所有的货物最终摆成一个大的长方体。即在长、宽、高的方向上分别堆 L、W、H 的货物,满足n=L×W×H。
给定 n,请问有多少种堆放货物的方案满足要求。
例如,当 n = 4 时,有以下 6 种方案:1×1×4、1×2×2、1×4×1、2×1×2、2 × 2 × 1、4 × 1 × 1。
请问,当 n = 2021041820210418(注意有 16位数字)时,总共有多少种方案?
提示:建议使用计算机编程解决问题。
分析:给出一个数n,求多少个三元组(L,W,H)使得L x W x H等于n。同时三元组是考虑顺序的,L,W,H是n的因数,即n % L == 0 && n % W == 0 && n % H == 0,为此,我们可以先将n的所有因数求出来,然后三重循环遍历L,W,H,若它们相乘等于n,则找到了一种方案。(暴力)
import java.util.*;// 1:无需package// 2: 类名必须Main, 不可修改public class Main {public static void main(String[] args) {// 常数默认值为int,告诉编译器它是long型常量long n = 2021041820210418l;int ans = 0;List<Long> l = new ArrayList<>();for(long i = 1; i <= Math.sqrt(n); i++){if(n % i == 0){l.add(i);if(i != n / i){l.add(n / i);}}}for(int i = 0; i < l.size(); i++){for(int j = 0; j < l.size(); j++){if(l.get(i) * l.get(j) > n){continue;}for(int k = 0; k < l.size(); k++){if(l.get(i) * l.get(j) * l.get(k) == n){ans++;}}}}System.out.println(ans);}}
输出答案:
2430
5. 路径
小蓝学习了最短路径之后特别高兴,他定义了一个特别的图,希望找到图 中的最短路径。
小蓝的图由 2021 个结点组成,依次编号 1 至 2021。
对于两个不同的结点 a, b,如果 a 和 b 的差的绝对值大于 21,则两个结点 之间没有边相连;如果 a 和 b 的差的绝对值小于等于 21,则两个点之间有一条 长度为 a 和 b 的最小公倍数的无向边相连。
例如:结点 1 和结点 23 之间没有边相连;结点 3 和结点 24 之间有一条无 向边,长度为 24;结点 15 和结点 25 之间有一条无向边,长度为 75。
请计算,结点 1 和结点 2021 之间的最短路径长度是多少。
提示:建议使用计算机编程解决问题。
分析:题目意思很明确,求源点到某个点的最短路径。最短路问题有两个常见的算法,Dijkstra算法和Floyed算法。本题适合用Dijkstra算法,为此我们先建图,图用二维矩阵e存储,dist数组表示源点到某个点的最短距离。最后输出dist[2021]的值。
import java.util.*;// 1:无需package// 2: 类名必须Main, 不可修改public class Main {public static void main(String[] args) {int[][] e = new int[2022][2022];int[] dist = new int[2022];boolean[] visit = new boolean[2022];Arrays.fill(dist, Integer.MAX_VALUE);for(int i = 1; i <= 2021; i++){for(int j = 1; j <= 2021; j++){if(i == j){e[i][j] = 0;}else{if(Math.abs(i - j) <= 21){e[i][j] = i * j / gcd(i, j);}else{e[i][j] = Integer.MAX_VALUE;}}}}dist[1] = 0;for(int i = 1; i <= 2021; i++){int u = 0, min = Integer.MAX_VALUE;for(int j = 1; j <= 2021; j++){if(!visit[j] && dist[j] < min){min = dist[j];u = j;}}visit[u] = true;for(int j = 1; j <= 2021; j++){if(e[u][j] != Integer.MAX_VALUE && dist[u] + e[u][j] < dist[j]){dist[j] = dist[u] + e[u][j];}}}System.out.println(dist[2021]);}static int gcd(int a, int b){return b == 0 ? a : gcd(b, a % b);}}
输出答案:
10266837
二:编程题
6. 时间显示
题目描述
小蓝要和朋友合作开发一个时间显示的网站。
在服务器上,朋友已经获取了当前的时间,用一个整数表示,值为从 19701970 年 11 月 11 日 00:00:0000:00:00 到当前时刻经过的毫秒数。
现在,小蓝要在客户端显示出这个时间。小蓝不用显示出年月日,只需要显示出时分秒即可,毫秒也不用显示,直接舍去即可。
给定一个用整数表示的时间,请将这个时间对应的时分秒输出。
输入描述
输入一行包含一个整数,表示时间。
输出描述
输出时分秒表示的当前时间,格式形如
HH:MM:SS
,其中HH
表示时,值为 00 到 2323,MM
表示分,值为 00 到 5959,SS
表示秒,值为 00 到 5959。时、分、秒 不足两位时补前导 00。输入输出样例
示例 1
输入
46800999
输出
13:00:00
示例 2
输入
1618708103123
输出
01:08:23
评测用例规模与约定
对于所有评测用例,给定的时间为不超过 10的18次方的正整数。
运行限制
- 最大运行时间:1s
- 最大运行内存: 512M
分析:签到题,注意1s=1000ms,先将ms转化为s,即n/1000(采用整除)。对于这一题,需理解取余的含义,即不需要知道它经历了多少年,算出它对于60s一分钟剩了多少秒,对于60min一小时剩了多少分钟,24h一天剩余了多少小时即可,剩余多少用取余表示。
import java.util.Scanner;// 1:无需package// 2: 类名必须Main, 不可修改public class Main {public static void main(String[] args) {Scanner scan = new Scanner(System.in);//在此输入您的代码...long n = scan.nextLong();n /= 1000;// 1s = 1000ms// 1min = 60s = 60000ms// 1h = 60min = 3600s = 3600000msSystem.out.printf("%02d:%02d:%02d", (n % (3600 * 24)) / 3600,(n % 3600) / 60,n % 60);scan.close();}}
7. 最少砝码
问题描述
你有一架天平。现在你要设计一套砝码,使得利用这些砝码可以称出任意 小于等于 N的正整数重量。
那么这套砝码最少需要包含多少个砝码?
注意砝码可以放在天平两边。
输入格式
输入包含一个正整数 N。
输出格式
输出一个整数代表答案。
样例输入
7
样例输出
3
样例说明
33 个砝码重量是 1、4、6,可以称出 1 至 7的所有重量。
1 = 1;
2 = 6 − 4(天平一边放 66,另一边放 44);
3 = 4 − 1;
4 = 4;
5 = 6 − 1;
6 = 6;
7 = 1 + 6;
少于 3 个砝码不可能称出 1 至 7 的所有重量。
评测用例规模与约定
对于所有评测用例,1 ≤ N ≤ 1000000000。
运行限制
- 最大运行时间:1s
- 最大运行内存: 512M
分析:思维题吧,如果以前没做过的话估计是想不到的,反正我看到之后第一反应是懵的。看到有的文章是这样推理的,称取质量1只需一个砝码1,第二个需要称取质量2,这时添加一个砝码1可以和第一个1组成2,添加砝码2除了可以组成2之外还可以组成3,添加砝码3可以组成2,3,4,添加砝码4的话就不能得到质量2因此不行,由贪心的思想第二个砝码还是选取砝码3好,然后可以依次退出规律。
应称重量 | 选择的砝码(choice) | 最终可称到的重量(total) |
---|---|---|
1 | 1 | 1 |
2 | 3 | 4 |
5 | 9 | 13 |
14 | 27 | 40 |
total+1 | choice*3 | choice*3 + total |
通过上表可以找出规律,而实际上,由首项为1,公比为3的等差数列,可以自由组成任意正整数。
import java.util.Scanner;// 1:无需package// 2: 类名必须Main, 不可修改public class Main {public static void main(String[] args) {Scanner scan = new Scanner(System.in);//在此输入您的代码...int n = scan.nextInt();int count = 1, now = 1, sum = 1;while(sum < n){count++;now *= 3;sum += now;}System.out.println(count);scan.close();}}
8. 杨辉三角形
下面的图形是著名的杨辉三角形:
如果我们按从上到下、从左到右的顺序把所有数排成一列,可以得到如下数列: 1, 1, 1, 1, 2, 1, 1, 3, 3, 1, 1, 4, 6, 4, 1, ⋯
给定一个正整数 N,请你输出数列中第一次出现 N是在第几个数?
输入描述
输入一个整数 N。
输出描述
输出一个整数代表答案。
输入输出样例
示例 1
输入
6
输出
13
评测用例规模与约定
对于 20% 的评测用例,1≤N≤10; 对于所有评测用例,1≤N≤1000000000。
运行限制
- 最大运行时间:1s
- 最大运行内存: 256M
分析:又是偏思维的题,这还是大家所讨论的暴力杯吗。虽然考试的时候,我们可能想不到能够AC的代码,但一定不能留空白,哪怕写的是纯暴力的代码也给它交上去,蓝桥杯是按点给分的(总共10个计分点),而且题目这不是明显的暗示吗——对于 20% 的评测用例,1≤N≤10。好了进入正题,杨辉三角实际上就是一个排列组合的表格。要是数据少的话我们还能通过极限打表给它打出来,但题目给出的n的最大值有10的9次方,为此我们需要尽可能的优化,毕竟优化之后可能又能多过一个点呢。优化第一步:从图中可以看出,每一行左边的数与右边的数对称,因此在遍历的时候,我们可以只遍历左边的图,且最大值第一次出现一定是在图的左边。但这样还是AC不了。优化第二步:考虑左边图形的斜行,第0斜行的开始是(C00),第1斜行的开始是(C21),第2斜行的开始是(C42)依次第n斜行的开始是(C2nn),且每一斜行是单调递增的,下面的斜行比上面的斜行大。可以计算到n取最大值也是在第16斜行可以遍历到,因此我们对于输入的n,从第16斜行往第0斜行遍历,直到第一次找到。对于每一斜行采用二分查找。哎,反正我是没想到这么巧妙的方法,长长见识。
import java.util.Scanner;// 1:无需package// 2: 类名必须Main, 不可修改public class Main {static int n;public static void main(String[] args) {Scanner scan = new Scanner(System.in);//在此输入您的代码...n = scan.nextInt();for(int i = 16;;i--){if(check(i)){break;}}scan.close();}static boolean check(int i){// 二分该斜行,找到大于等于该值的第一个数// 左边界2k,右边界为max(l, n)取二者最大,避免右边界小于左边界int r = 2 * i, l = Math.max(n,r);while(r < l){int mid = (l - r) / 2 + r;if(C(mid, i) >= n) l = mid;else r = mid + 1;}if(C(r, i) != n)return false;else{// 1L * (r + 1) * r / 2中这个1L是不能少的,因为r是int类型的,如果没有1L将表达式的值提高到// long的话,表达式的值会先溢出,这时在强制转化为long没有作用System.out.println((long) (1l * (r + 1) * r / 2) + i + 1);return true;}}static long C(int a, int b){long ans = 1;for(int i = a, j = 1; j <= b; j++, i--){ans = ans * i / j;if(ans > n){// 避免ans超过long的最大值return ans;}}return ans;}}
9. 双向排序
题目链接:https://www.lanqiao.cn/problems/1458/learning/
博客链接:https://blog.csdn.net/qq_35975367/article/details/116277383
说实话看不太懂……
10. 括号序列
题目链接:https://www.lanqiao.cn/problems/1456/learning/
博客链接:https://blog.csdn.net/qq_51972900/article/details/116428039
从上面这套真题可以看出蓝桥杯的题目难易分明,也不是大家口中常说的暴力杯。虽然说确实有签到送分题,但也不会缺少思维题(好难啊),对于思维题是在想不出的话还是暴力拿部分样例分吧……