第12届蓝桥杯JavaB组省赛
前言
用时及分数
自测开始时间:\(2023年1月11日 16:00\)
自测结束时间:\(2023年1月11日 18:05\)
自测用时:约 \(2\) 小时
最终得分:\(20(35)\)
PS:此处得分依据蓝桥杯官网OJ测得
题目情况
完成题目:\(ABC(F)\)
\(F\) 题提交时的类名未使用 \(Main\),不得分。
\(D\) 题暴力写法跑了 \(30\) 分钟没跑出来
因为干饭提前结束测试
\(E\) 最短路没学或者说忘了
感受
\(12\) 届的题目填空题居然有 \(5\) 道,\(13\) 届才 \(2\) 道
而且感觉 \(13\) 届比 \(12\) 届更偏向算法知识了
注意点
-
提交时一定要注意类名等情况,或者建文件时就建多个 \(Main\)
-
心态放平,时间很充裕
-
实在写不出来正解,也尽量把暴力写出来
-
一定要把题目都看完
-
填空题提交之前一定要先对小数据进行测试
试题 A ASC
问题描述
已知大写字母 \(A\) 的 \(ASCII\) 码为 \(65\),请问大写字母 \(L\) 的 \(ASCII\) 码是多少?
答案为:\(76\)
Code
public class Main {
public static void main(String[] args) {
System.out.println((int) ('L'));//76
}
}
试题 B 卡片
问题描述
小蓝有很多数字卡片,每张卡片上都是数字 \(0\) 到 \(9\)。
小蓝准备用这些卡片来拼一些数,他想从 \(1\) 开始拼出正整数,每拼一个,就保存起来,卡片就不能用来拼其它数了。
小蓝想知道自己能从 \(1\) 拼到多少。
例如,当小蓝有 \(30\) 张卡片,其中 \(0\) 到 \(9\) 各 \(3\) 张,则小蓝可以拼出 \(1\) 到 \(10\),但是拼 \(11\) 时卡片 \(1\) 已经只有一张了,不够拼出 11。
现在小蓝手里有 \(0\) 到 \(9\) 的卡片各 \(2021\) 张,共 \(20210\) 张,请问小蓝可以从 \(1\) 拼到多少?
提示:建议使用计算机编程解决问题。
答案为:\(3181\)
思路
一个数组记录当前对应类型的牌剩余多少张,每算一个数字就减去所需的牌数
import java.util.Arrays;
public class Main {
public static void main(String[] args) {
int[] arr = new int[10];
Arrays.fill(arr, 2021);
int now = 1;
while (true) {
int t = now;
while (t != 0) {
if (arr[t % 10] > 0) --arr[t % 10];
else {
//这里要输出now-1,因为当前这个牌是拼不出来的!!!
System.out.println(now-1);//3181
return;
}
t /= 10;
}
++now;
}
}
}
试题 C 直线
问题描述
在平面直角坐标系中,两点可以确定一条直线。如果有多点在一条直线上,那么这些点中任意两点确定的直线是同一条。
给定平面上 \(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\)) 之间的整数的点。请问这些点一共确定了多少条不同的直线。
答案为:\(40257\)
思路
既然是填空题,比赛的时候能暴力解就暴力解
既能很快的得到答案,也能减少出错的概率
具体思路见注释
import java.util.LinkedList;
import java.util.List;
public class Main {
static class Point {
public int x, y;
public Point(int x, int y) {
this.x = x;
this.y = y;
}
}
static class Line {
public Point A, B;
public Line(Point A, Point B) {
this.A = A;
this.B = B;
}
}
static List<Line> list = new LinkedList<>();
//判断点是否在该直线上
public static boolean checkPoint(Point p, Line l) {
return (p.y - l.A.y) * (p.x - l.B.x) == (p.y - l.B.y) * (p.x - l.A.x);
}
//判断两条直线是否为同一条
public static boolean checkLine(Line a, Line b) {
//先判断斜率是否相等
if ((a.B.y - a.A.y) * (b.B.x - b.A.x) != (b.B.y - b.A.y) * (a.B.x - a.A.x)) return false;
//如果斜率相等再取直线上一点,是否在另一直线上
return checkPoint(a.A, b);
}
//判断列表中是否存在该直线
public static boolean check(Line a) {
for (Line l : list) {
if (checkLine(a, l)) {
return true;
}
}
return false;
}
public static void main(String[] args) {
final int X = 20, Y = 21;
for (int x1 = 0; x1 < X; ++x1) {
for (int y1 = 0; y1 < Y; ++y1) {
Point a = new Point(x1, y1);
for (int x2 = 0; x2 < X; ++x2) {
for (int y2 = 0; y2 < Y; ++y2) {
//如果两点重合则不选
if (x2 == x1 && y2 == y1) continue;
// System.out.printf("a=(%d,%d),b=(%d,%d)\n", x1, y1, x2, y2);
Point b = new Point(x2, y2);
Line l = new Line(a, b);
if (!check(l)) list.add(l);
}
}
}
}
System.out.println(list.size());//40257
}
}
试题 D 货物摆放
问题描述
小蓝有一个超大的仓库,可以摆放很多货物。
现在,小蓝有 \(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\) 位数字)时,总共有多少种方案?
提示:建议使用计算机编程解决问题。
答案为:\(2430\)
思路一
遍历第一个数 \(L\),如果是 \(n\) 的因子,说明当前数可取,然后遍历第二个数 \(W\)
由于 \(n = L × W × H\),当知道前两个数时,第 \(3\) 个数也随之确定
当 \(W\ne H\) 时,\(W\) 的值与 \(H\) 的值交换算是两种方案
因此,只需确定 \(W< \sqrt{\dfrac{n}{L}}\) 的 \(W\) 取值数量,然后乘以 \(2\)
再单独确定 \(W=\sqrt{\dfrac{n}{L}}\) 时是否满足条件,此时只算做一种方案
程序运行时间很长,最后没跑出来
public class Main {
public static void main(String[] args) {
final long n = 2021041820210418l;
long ans = 0;
for (long l = 1; l <= n; ++l) {
if (n % l != 0) continue;
final long rest = n / l;
long w;
for (w = 1; w * w < rest; ++w) {
if (rest % w == 0) ans += 2;
}
if (w * w == rest) ++ans;
}
System.out.println(ans);
}
}
思路二
比赛时最开始想到的思路
先处理出 \(n\) 的所有因子,再三重循环(或 \(DFS\))选出三个因子,相乘是否等于 \(n\)
public class Main {
public static void main(String[] args) {
long[] num = new long[1000];
int cnt = 0;
final long n = 2021041820210418l;
long now = 1;
while (now * now < n) {
if (n % now == 0) {
num[cnt++] = now;
num[cnt++] = n / now;
}
++now;
}
long ans = 0;
for (int i = 0; i < cnt; ++i) {
for (int j = 0; j < cnt; ++j) {
for (int k = 0; k < cnt; ++k) {
if (num[i] * num[j] * num[k] == n) {
++ans;
}
}
}
}
System.out.println(ans);//2430
}
}
思路三
若 \(L\leq W\leq H\),则:
- 当 \(L=W=H\) 时,生成方案数为 \(1\)
- 当 \(L=W < H\) 或 \(L< W=H\) 时,生成方案数为 \(3\)
- 当 \(L<W<H\) 时,生成方案数为 \(6\)
public class Main {
public static void main(String[] args) {
final long n = 2021041820210418l;
long ans = 0;
for (long i = 1; i * i * i <= n; ++i) {
if (n % i != 0) continue;
long rest = n / i;
for (long j = i; j * j <= rest; ++j) {
if (rest % j != 0) continue;
long k = rest / j;
if (i < j && j < k) ans += 6;
else if (i == j && j == k) ans += 1;
else ans += 3;
}
}
System.out.println(ans);//2430
}
}
试题 E 路径
问题描述
小蓝学习了最短路径之后特别高兴,他定义了一个特别的图,希望找到图中的最短路径。
小蓝的图由 \(2021\) 个结点组成,依次编号 \(1\) 至 \(2021\)。
对于两个不同的结点 \(a, b\),如果 \(a\) 和 \(b\) 的差的绝对值大于 \(21\),则两个结点之间没有边相连;
如果 \(a\) 和 \(b\) 的差的绝对值小于等于 \(21\),则两个点之间有一条长度为 \(a\) 和 \(b\) 的最小公倍数的无向边相连。
例如:结点 \(1\) 和结点 \(23\) 之间没有边相连;结点 \(3\) 和结点 \(24\) 之间有一条无向边,长度为 \(24\);结点 \(15\) 和结点 \(25\) 之间有一条无向边,长度为 \(75\)。
请计算,结点 \(1\) 和结点 \(2021\) 之间的最短路径长度是多少。
提示:建议使用计算机编程解决问题。
答案为:\(10266837\)
思路
很明显最短路算法
数据范围很小采用邻接矩阵存图
\(Floyd\) 求全源最短路
import java.util.Arrays;
public class Main {
static int gcd(int x, int y) {
return y == 0 ? x : gcd(y, x % y);
}
static int lcm(int x, int y) {
return x / gcd(x, y) * y;
}
static final int N = 2021;
static final int INF = (int) 1e9;
//dis[i][j]表示i->j的路径长度
static int[][] dis = new int[N + 1][N + 1];
//运行Floyd之后,dis[i][j]变为 i->j的最短路径长度
static void Floyd() {
for (int k = 1; k <= N; ++k)
for (int i = 1; i <= N; ++i)
for (int j = 1; j <= N; ++j)
dis[i][j] = Math.min(dis[i][j], dis[i][k] + dis[k][j]);
}
public static void main(String[] args) {
for (int i = 0; i <= N; ++i) Arrays.fill(dis[i], INF);
for (int i = 1; i <= N; ++i) {
for (int j = i + 1; j <= N; ++j) {
if (j - i <= 21) {
dis[i][j] = dis[j][i] = lcm(i, j);
}
}
}
Floyd();
System.out.println(dis[1][2021]);//10266837
}
}
\(Dijkstra\) 求单源最短路
待补充
试题 F 时间显示
问题描述
小蓝要和朋友合作开发一个时间显示的网站。在服务器上,朋友已经获取了当前的时间,用一个整数表示,值为从 \(1970\) 年 \(1\) 月 \(1\) 日 \(00:00:00\) 到当前时刻经过的毫秒数。
现在,小蓝要在客户端显示出这个时间。小蓝不用显示出年月日,只需要显示出时分秒即可,毫秒也不用显示,直接舍去即可。
给定一个用整数表示的时间,请将这个时间对应的时分秒输出。
【输入格式】
输入一行包含一个整数,表示时间。
【输出格式】
输出时分秒表示的当前时间,格式形如 \(HH:MM:SS\),其中 \(HH\) 表示时,值为 \(0\) 到 \(23\),\(MM\) 表示分,值为 \(0\) 到 \(59\),\(SS\) 表示秒,值为 \(0\) 到 \(59\)。
时、分、秒不足两位时补前导 0。
【样例输入 1】
46800999
【样例输出 1】
13:00:00
【样例输入 2】
1618708103123
【样例输出 2】
01:08:23
【评测用例规模与约定】
对于所有评测用例,给定的时间为不超过 \(10^{18}\) 的正整数
Code
import java.util.Scanner;
public class F {
//计算一天有多少毫秒
static final long dayMS = 24 * 60 * 60 * 1000;
public static void main(String[] args) {
long n = new Scanner(System.in).nextLong() % dayMS;
long h = n / (60 * 60 * 1000);
long m = n / (60 * 1000) % 60;
long s = n / 1000 % 60;
System.out.println(String.format("%02d:%02d:%02d", h, m, s));
}
}
试题 G 最少砝码
问题描述
你有一架天平。现在你要设计一套砝码,使得利用这些砝码可以称出任意小于等于 \(N\) 的正整数重量。
那么这套砝码最少需要包含多少个砝码?
注意砝码可以放在天平两边。
思路
原文链接:
import java.util.Scanner;
public class Main {
public static void main(String[] args) {
int n = new Scanner(System.in).nextInt();
int weight = 1, count = 1, ans = 1;
while (count < n) {
weight *= 3;
count += weight;
++ans;
}
System.out.println(ans);
}
}
参考资料
第十二届蓝桥杯省赛JavaB组 试题 G: 最少砝码_小小风0的博客-CSDN博客
第十二届蓝桥杯省赛 Java B组 试题 G: 最少砝码_山雾野灯的博客-CSDN博客
标签:12,int,long,蓝桥,++,static,now,public,组省赛 From: https://www.cnblogs.com/Cattle-Horse/p/17044968.html