试题E:路径,添加了Dijkstra算法,秒出答案
试题 A: ASC(5分)
已知大写字母 A 的 ASCII 码为 65,请问大写字母 L 的 ASCII 码是多少?
答案: 76
1 2 3 4 5 6 public class Main { public static void main (String[] args) { System.out.println((int )'L' ); } }
试题 B: 卡片(5分)
小蓝有很多数字卡片,每张卡片上都是数字 0 到 9。
小蓝准备用这些卡片来拼一些数,他想从 1 开始拼出正整数,每拼一个, 就保存起来,卡片就不能用来拼其它数了。
小蓝想知道自己能从 1 拼到多少。
例如,当小蓝有 30 张卡片,其中 0 到 9 各 3 张,则小蓝可以拼出 1 到 10, 但是拼 11 时卡片 1 已经只有一张了,不够拼出 11。
现在小蓝手里有 0 到 9 的卡片各 2021 张,共 20210 张,请问小蓝可以从 1 拼到多少?
答案: 3181
思路
思路是哈希
模拟一遍这个流程就好了
创建一个长度为10的数组 将数组中的每个元素初始化为2021 代表0~9 总共十种卡片 每种有2021张
然后将数字转化成字符串 字符串再转化成字符数组 遍历字符数组 将每个字符对应的卡片张数-1
当遍历完某个数字之后 卡片张数变为负数 则当前数字无法拼出来 输出当前数字的上一个数字即可
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 import java.util.Arrays;public class Main { public static void main (String[] args) { int [] cards = new int [10 ]; Arrays.fill(cards, 2021 ); int num = 1 ; boolean flag = true ; while (flag) { String str = num + "" ; char [] c = str.toCharArray(); for (char ch : c) { cards[ch - '0' ] -= 1 ; if (cards[ch - '0' ] < 0 ) { System.out.println(num - 1 ); flag = false ; break ; } } num++; } } }
试题 C: 直线(10分)
在平面直角坐标系中,两点可以确定一条直线。如果有多点在一条直线上, 那么这些点中任意两点确定的直线是同一条。
给定平面上 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
思路
直线的斜截式 y=kx+b 只要每条直线的k和b不同 那就不是同一条直线
由于涉及到小数问题 直接比较大小会出错 这里我才用保留八位有效数字
浮点数比较大小 只要差值小于1e-8 即可认为相等
新建一个Fx类和Point类
Point类用来存放每一个点,遍历Point集合,将任意两点组成直线的k和b计算出来,并存入res集合
Fx类用来记录每两个点产生直线的k和b 并将它存入sdt集合中去重 平行于X轴和Y轴的单独处理 最后加上就行
HashSet去重:先判断hashCode()是否相同,相同才会判断equals()
如果是需要对我们自定义的对象去重,就需要我们重写 hashCode 和 equals 方法
注意:HashSet要求放入的对象必须重写hashCode() ,不然HashSet调用默认的hashCode方法判断对象的地址 ,不等就达不到想根据对象的值去重的目的。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 import java.text.DecimalFormat;import java.util.*;class Solution { public static void main (String[] args) { ArrayList<Point> points = new ArrayList <>(); for (int i = 0 ; i < 20 ; i++) { for (int j = 0 ; j < 21 ; j++) { points.add(new Point (i, j)); } } HashSet<Fx> set = new HashSet <>(); for (Point p1 : points) { for (Point p2 : points) { if (p1.x != p2.x && p1.y != p2.y){ double k = (p1.y - p2.y) / (p1.x - p2.x); double b = p1.y - k * p1.x; set.add(new Fx (k, b)); } } } System.out.println(set.size() + 20 + 21 ); } } class Point { double x; double y; public Point (double x, double y) { this .x = x; this .y = y; } } class Fx { double k; double b; public Fx (double k, double b) { DecimalFormat df = new DecimalFormat ("0.00000000" ); this .k = Double.parseDouble(df.format(k)); this .b = Double.parseDouble(df.format(b)); } @Override public boolean equals (Object o) { if (this == o) return true ; if (!(o instanceof Fx)) return false ; Fx fx = (Fx) o; return Double.compare(fx.k, k) == 0 && Double.compare(fx.b, b) == 0 ; } @Override public int hashCode () { return Objects.hash(k, b); } }
试题 D: 货物摆放(10分)
小蓝有一个超大的仓库,可以摆放很多货物。
现在,小蓝有 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
思路
计算出因数然后无脑暴力三重循环
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 import java.util.HashSet;public class Main { public static void main (String[] args) { long num = 2021041820210418L ; HashSet<Long> set = new HashSet <>(); for (long i = 1 ; i < Math.sqrt(num); i++) { if (num % i == 0 ) { set.add(i); set.add(num / i); } } int res = 0 ; for (long a : set) for (long b : set) for (long c : set) if (a * b * c == num) res++; System.out.println(res); } }
试题 E: 路径(15分)
小蓝学习了最短路径之后特别高兴,他定义了一个特别的图,希望找到图 中的最短路径。
小蓝的图由 2021 个结点组成,依次编号 1 至 2021。
对于两个不同的结点 a, b,如果 a 和 b 的差的绝对值大于 21,则两个结点 之间没有边相连;如果 a 和 b 的差的绝对值小于等于 21,则两个点之间有一条 长度为 a 和 b 的最小公倍数的无向边相连。
例如:结点 1 和结点 23 之间没有边相连;结点 3 和结点 24 之间有一条无 向边,长度为 24;结点 15 和结点 25 之间有一条无向边,长度为 75。
请计算,结点 1 和结点 2021 之间的最短路径长度是多少。
**答案:**10266387
思路
先按题目要求建图,将所有节点初始化为0x3f3f3f3f
(0x3f3f3f3f的十进制是1061109567,是10^9级别的,而一般场合下的数据都是小于10^9的,所以它可以作为无穷大使用)
两个节点的绝对值小于等于21 则求最小公倍数
最小公倍数公式 lcm = a * b / (gcd(a, b))
lcm是最小公倍数 gcd是最大公约数
欧几里得法求最大公约数 gcd(a, b) = gcd(b, a % b)
然后用弗洛伊德算法 求最短路径即可
(时间复杂度n^3 仅限填空题可以用用 编程题还是要用迪杰斯特拉算法)
心里默念了一下,大概算了10秒…
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 public class Main { static int [][] arr = new int [2030 ][2030 ]; static void Floyd () { for (int k = 1 ; k <= 2021 ; k++) for (int i = 1 ; i <= 2021 ; i++) for (int j = 1 ; j <= 2021 ; j++) if (arr[i][j] > arr[i][k] + arr[k][j]) arr[i][j] = arr[i][k] + arr[k][j]; } static int gcd (int a, int b) { return b == 0 ? a : gcd(b, a % b); } public static void main (String[] args) { for (int i = 1 ; i <= 2021 ; i++) { for (int j = 1 ; j <= 2021 ; j++) { if (Math.abs(i - j) <= 21 ) arr[i][j] = i * j / gcd(i, j); else arr[i][j] = 0x3f3f3f3f ; } } Floyd(); System.out.println(arr[1 ][2021 ]); } }
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 import java.util.Arrays;public class Main { static int [] dist = new int [2030 ]; static int [][] graph = new int [2030 ][2030 ]; static boolean [] used = new boolean [2030 ]; static int Dijkstra () { for (int i = 1 ; i <= 2021 ; i++) { int minIndex = -1 ; for (int j = 1 ; j <= 2021 ; j++) if (!used[j] && (minIndex == -1 ||dist[j] < dist[minIndex])) minIndex = j; used[minIndex] = true ; for (int j = 1 ; j <= 2021 ; j++) { dist[j] = Math.min(dist[j], dist[minIndex] + graph[minIndex][j]); } } return dist[2021 ]; } static int gcd (int a, int b) { return b == 0 ? a : gcd(b, a % b); } public static void main (String[] args) { Arrays.fill(dist, 0x3f3f3f3f ); dist[1 ] = 0 ; for (int i = 1 ; i <= 2021 ; i++) { for (int j = 1 ; j <= 2021 ; j++) { if (Math.abs(i - j) <= 21 ) graph[i][j] = i * j / gcd(i, j); else graph[i][j] = 0x3f3f3f3f ; } } System.out.println(Dijkstra()); } }
试题 F: 时间限制(15分)
小蓝要和朋友合作开发一个时间显示的网站。在服务器上,朋友已经获取 了当前的时间,用一个整数表示,值为从 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的正整数。
代码
1 2 3 4 5 6 7 8 9 10 import java.util.Scanner;public class Main { public static void main (String[] args) { Scanner scan = new Scanner (System.in); long time = scan.nextLong(); time = time / 1000 % (24 * 60 * 60 ); System.out.printf("%02d:%02d:%02d" , time / 3600 , time / 60 % 60 , time % 60 ); } }
试题 G: 砝码称重(20分)
你有一架天平和 N 个砝码,这 N 个砝码重量依次是 W1,W2,⋅⋅⋅,WN。
请你计算一共可以称出多少种不同的正整数重量?
注意砝码可以放在天平两边。
输入格式
输入的第一行包含一个整数 N。
第二行包含 N 个整数:W1,W2,W3,⋅⋅⋅,WN。
输出格式
输出一个整数代表答案。
输入样例:
3
1 4 6
输出样例:
10
样例解释
能称出的 10 种重量是:1、2、3、4、5、6、7、9、10、11。
1 = 1;
2 = 6 − 4 (天平一边放 6,另一边放 4);
3 = 4 − 1;
4 = 4;
5 = 6 − 1;
6 = 6;
7 = 1 + 6;
9 = 4 + 6 − 1;
10 = 4 + 6;
11 = 1 + 4 + 6。
数据范围
对于 50% 的评测用例,1≤N≤15。
对于所有评测用例,1≤N≤100,N 个砝码总重不超过 100000。
思路
网上看别的题解都用的是dp,我这里有个不用dp的方法
建立一个set集合 用来去重
我们先将第一个砝码a存入set集合
每个砝码只有三种情况 放左边 放右边 不放
那对于第二个砝码b 我们有 a+b a-b b 三种情况
我们把这三种情况的结果放入set
然后再用一个list集合遍历即可
每次放一个砝码之前 都将set集合中的结果 初始化到list集合中
用list集合中的每个元素,对砝码的三种情况进行计算,并放入set集合中
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 import java.util.ArrayList;import java.util.HashSet;import java.util.Scanner;public class Main { public static void main (String[] args) { Scanner scan = new Scanner (System.in); int n = scan.nextInt(); HashSet<Integer> set = new HashSet <>(); for (int i = 0 ; i < n; i++) { int a = scan.nextInt(); ArrayList<Integer> tmp = new ArrayList <>(set); for (int b : tmp) { set.add(b + a); if (b != a) set.add(Math.abs(b - a)); } set.add(a); } System.out.println(set.size()); } }
试题 H: 杨辉三角形(20分)
HE
思路
杨辉三角的第i行第j个的数都是组合数C(i, j) (i,j从0开始)
C(n, 1) = n 对应斜着的第二行 也就是一定有解
由于杨辉三角左右对称C(a, b) == C(a, a-b),又由于找第一次出现,因此一定在左边,右边不用看
1 ---> C(0, 0)
1
1 2 ---> C(2, 1)
1 3 ---> C(2n, n)
1 4 6 ---> C(4, 2)
1 5 10
1 6 15 20 —> C(6, 3)
题目范围上限1E9,C(34, 17) > 1E9, C(32, 16) < 1E9,因此只要枚举前16个斜行即可
每个斜行是依次递增的C(n,m) m相同n越大 C(n,m)越大
每个横行从中间到两边是递减的
因此我们直接从中间对称轴倒序二分找起即可
C(r, k)对应的位置为:(r + 1) * r / 2 + k + 1
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 import java.util.*;public class Main { static int target; static long C (int a, int b) { long res = 1 ; for (int i = a, j = 1 ; j <= b; i--, j++) { res = res * i / j; if (res > target) return res; } return res; } static boolean check (int line) { if (target == 1 ) { System.out.println(1 ); return true ; } int left = 2 * line; int right = target; while (left < right) { int mid = left + right >> 1 ; if (C(mid, line) >= target) right = mid; else left = mid + 1 ; } if (C(left, line) != target) return false ; System.out.println((long ) right * (right + 1 ) / 2 + line + 1 ); return true ; } public static void main (String[] args) { Scanner scan = new Scanner (System.in); target = scan.nextInt(); int line = 16 ; while (line-- > 0 ) { if (check(line)) break ; } } }
试题 I: 双向排序(25分)
思路
暴力解法会超时 所以这个题还是要找规律的(比赛肯定想不出来 )
如果有两个连续 的指令都是让我们降序排序,比如0 3和0 5,那么0 3这条指令就不需要进行操作,我们只需要执行操作区间更大的0 5
{ 1 2 3 4 5 6 7 }
0 3
{ 3 2 1 4 5 6 7 }
0 5
{ 5 4 3 2 1 6 7 }
再观察一下下面的两个操作
{ 1 2 3 4 5 6 7 }
0 5
{ 5 4 3 2 1 6 7 }
1 4
{ 5 4 3 1 2 6 7 }
0 5的意义就是将[1, 5]这个区间进行降序操作,而一开始整个区间[1, 7]都是升序的,我们要让[1,5]降序,那就只需要取[1,7]和[1,5]的交集,然后翻转这个交集,显然这个交集就是[1, 5],翻转[1, 5],于是我们得到了一个降序的[1, 5]区间,1 4的意义就是将[4, 7]这个区间进行升序操作,而最右边的区间[6, 7]已经是升序的了,并且里面所有的数字都比它们左边所有的数字都要大,**所以[6, 7]区间的数字并不会变化,**我们只需要接着取交集,取[1, 5]和[4, 7]的交集,得到一个[4, 5]的区间,翻转[4, 5]就是我们最终的答案。
要是上面两个操作之后,突然出现一个0 7的指令怎么办 显然执行0 7会覆盖之前两步的操作
所以不是连续的0操作最大就行了 而是要让后面比较大的0操作 覆盖掉之前操作区间所有比它小的操作 其间的1和0操作都会因为这个比较大的0操作而变得无效
经过上述这两步 我们就可以去掉很多无效操作 只保留有效操作 将这些有效操作入栈
栈维护出来之后,区间外部的东西就永远都不会动了,所以只需要一边缩区间一边填数字就行了
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 import java.util.Scanner;public class Main { static int N = 100010 ; static int n, m; static PII[] stk = new PII [N]; static int [] ans = new int [N]; public static void main (String[] args) { Scanner sc = new Scanner (System.in); n = sc.nextInt(); m = sc.nextInt(); int top = 0 ; while (m -- > 0 ) { int p = sc.nextInt(); int q = sc.nextInt(); if (p == 0 ) { while (top != 0 && stk[top].x == 0 ) q = Math.max(q, stk[top -- ].y); while (top >= 2 && stk[top - 1 ].y <= q) top -= 2 ; stk[ ++ top] = new PII (0 , q); } else if (top != 0 ) { while (top != 0 && stk[top].x == 1 ) q = Math.min(q, stk[top --].y); while (top >= 2 && stk[top - 1 ].y >= q) top -= 2 ; stk[ ++ top] = new PII (1 , q); } } int k = n, l = 1 , r = n; for (int i = 1 ; i <= top; i ++ ) { if (stk[i].x == 0 ) { while (r > stk[i].y && l <= r) ans[r -- ] = k --; } else { while (l < stk[i].y && l <= r) ans[l ++ ] = k --; } if (l > r) break ; } if (top % 2 == 1 ) { while (l <= r) ans[l ++ ] = k --; } else { while (l <= r) ans[r -- ] = k --; } for (int i = 1 ; i <= n; i ++ ) System.out.print(ans[i] + " " ); } static class PII { int x, y; public PII (int x, int y) { this .x = x; this .y = y; } } } import java.util.Arrays;import java.util.Comparator;import java.util.Scanner;public class Main { public static void main (String[] args) { Scanner scan = new Scanner (System.in); int n = scan.nextInt(); Integer[] arr = new Integer [n]; for (int i = 0 ; i < n; i++) { arr[i] = i + 1 ; } int m = scan.nextInt(); for (int i = 0 ; i < m; i++) { int p = scan.nextInt(); int split = scan.nextInt(); if (p == 0 ) { Arrays.sort(arr, 0 , split, (o1, o2) -> o2 - o1); } else { Arrays.sort(arr, split - 1 , n); } } for (int i = 0 ; i < n; i++) { System.out.print(arr[i]+" " ); } } }
试题 J: 括号序列(25分)
摆烂了 有空再说