2023年第十四届蓝桥杯大赛软件赛省赛Java 大学 B组
https://www.lanqiao.cn/paper/
请自行到官方平台查看题干
A- 阶乘求和
基础阶乘:
import java.util.Scanner;
public class 基础阶乘 {
public static void main(String[] args) {
Scanner sc=new Scanner(System.in);
int n=sc.nextInt();
long num=阶乘(n);
System.out.println(num);
}
public static long 阶乘(int n){
if(n==0||n==1){
return 1;
}
if(n==2){
return 2;
}
return 阶乘(n-1)*n;
}
}
大数阶乘:
import java.math.BigInteger;
import java.util.ArrayList;
import java.util.Scanner;
public class 大数阶乘 {
public static void main(String[] args) {
Scanner sc=new Scanner(System.in);
int n=sc.nextInt();
BigInteger num=阶乘(n);
System.out.println(num);
}
public static BigInteger 阶乘(int n) {
ArrayList list=new ArrayList();
list.add(BigInteger.valueOf(1));
for(int i=list.size();i<=n;i++) {
BigInteger lastfact=(BigInteger) list.get(i-1);
BigInteger nextfact=lastfact.multiply(BigInteger.valueOf(i));
list.add(nextfact);
}
return (BigInteger) list.get(n);
}
}
=========================================================================
public static BigInteger 阶乘2(int n) {
BigInteger factorial = BigInteger.valueOf(1); // 初始化为 0!
for (int i = 1; i <= n; i++) {
factorial = factorial.multiply(BigInteger.valueOf(i));
}
return factorial;
}
分析:
- 202320232023 这个数太大,暴力不可能找到对应可以表示的数据类型,从找规律下手
n | n的阶乘 |
---|---|
1 | 1 |
2 | 2 |
3 | 6 |
4 | 24 |
5 | 120 |
6 | 720 |
7 | 5040 |
8 | 40320 |
9 | 362880 |
10 | 3628800 |
11 | 39916800 |
12 | 479001600 |
13 | 6227020800 |
14 | 6227020800 |
15 | 1307674368000 |
16 | 20922789888000 |
17 | 355687428096000 |
18 | 6402373705728000 |
19 | 121645100408832000 |
20 | 2432902008176640000 |
21 | -4249290049419214848(栈溢出了 无效数字) 51090942171709440000【换成大数阶乘】 |
22 | 1124000727777607680000 |
23 | 25852016738884976640000 |
24 | 620448401733239439360000 |
25 | 15511210043330985984000000 |
26 | 403291461126605635584000000 |
27 | 10888869450418352160768000000 |
28 | 304888344611713860501504000000 |
29 | 8841761993739701954543616000000 |
30 | 265252859812191058636308480000000 |
31 | 8222838654177922817725562880000000 |
32 | 263130836933693530167218012160000000 |
33 | 8683317618811886495518194401280000000 |
34 | 295232799039604140847618609643520000000 |
35 | 10333147966386144929666651337523200000000 |
36 | 371993326789901217467999448150835200000000 |
37 | 13763753091226345046315979581580902400000000 |
38 | 523022617466601111760007224100074291200000000 |
39 | 20397882081197443358640281739902897356800000000 |
40 | 815915283247897734345611269596115894272000000000 !!!!9个0了 |
41 | 33452526613163807108170062053440751665152000000000 |
42 | 1405006117752879898543142606244511569936384000000000 |
43 | 60415263063373835637355132068513997507264512000000000 |
- 如上表格可见,n的阶乘末尾0的个数越来越多,可以推测到 202320232023!的末尾远远不止9个0
- 因为需要求的是阶乘和,那么从1!+……+40!的结果取末9位数跟取1!+……+202320232023!的结果取末位数相同
- 注:
/ 10^9
考虑前九位,% 10^9
保留后9位
修改大数阶乘代码 计算阶乘和
import java.math.BigInteger;
import java.util.ArrayList;
import java.util.Scanner;
public class 大数阶乘求和 {
public static void main(String[] args) {
Scanner sc = new Scanner(System.in);
int n = sc.nextInt();
BigInteger sum = 阶乘求和(n);
System.out.println(sum);
}
public static BigInteger 阶乘求和(int n) {
BigInteger sum = BigInteger.valueOf(0);
ArrayList<BigInteger> list = new ArrayList<>();
list.add(BigInteger.valueOf(1));// 0!
for (int i = 1; i <= n; i++) {
BigInteger lastfact = list.get(i - 1);
BigInteger nextfact = lastfact.multiply(BigInteger.valueOf(i));
list.add(nextfact);
}
// for (BigInteger fact : list) {
// sum = sum.add(fact);
// }
// 从列表的第二个元素开始累加,因为第一个元素是 0!
for (int i = 1; i <= n; i++) {
sum = sum.add(list.get(i));
}
return sum;
}
}
//40
//836850334330315506193242641144055892504|420940313
public static BigInteger 阶乘求和2(int n) {
BigInteger sum = BigInteger.valueOf(0);
BigInteger factorial = BigInteger.valueOf(1); // 初始化为 0!
for (int i = 1; i <= n; i++) {
sum = sum.add(factorial); // 将当前阶乘值加到总和中
factorial = factorial.multiply(BigInteger.valueOf(i + 1)); // 计算下一个阶乘
}
return sum;
}
结果就是:420940313
B-幸运数字
本题考的是进制转换,手写或者熟练使用Java的API都行
//借助API
public class Main {
public static void main(String[] args) {
int cnt = 0; //记录成立个数
int num = 1; //判断数字
while (true){
if(isB(num) && isO(num) && isD(num) && isH(num)){//2, 8, 10, 16 进制判断
cnt++;
}
if(cnt == 2023){ // 到 2023 个成立的数输出并退出
System.out.println(num);
break;
}
num++;
}
}
public static boolean isD(int num){
int sum = 0;
int t = num;
while (t != 0){
sum += t % 10;
t /= 10;
}
return num % sum == 0;
}
public static boolean isB(int num){
int sum = 0;
String t = Integer.toBinaryString(num); // api 转换后是 String 类型需要用字符串操作
for(int i = 0; i < t.length(); i++){
sum += t.charAt(i) - '0';
}
return num % sum == 0;
}
public static boolean isO(int num){
int sum = 0;
String t = Integer.toOctalString(num);
for(int i = 0; i < t.length(); i++){
sum += t.charAt(i) - '0';
}
return num % sum == 0;
}
public static boolean isH(int num){
int sum = 0;
String t = Integer.toHexString(num);
for(int i = 0; i < t.length(); i++){
if(t.charAt(i) >= 'a'){
sum += t.charAt(i) - 'a' + 10; // 字母情况特殊判断
}else{
sum += t.charAt(i) - '0';
}
}
return num % sum == 0;
}
}
//手写计算
public class Main {
public static void main(String[] args) {
int cnt = 0;
int num = 1;
while (true) {
if (check(2, num) && check(8, num) && check(10, num) && check(16, num)) {
cnt++;
}
if (cnt == 2023) {
System.out.println(num);
break;
}
num++;
}
}
public static boolean check(int base, int num){ // 进制位, 转换数
int t = num;
int sum = 0;
while (t != 0){
sum += t % base;
t /= base;
}
return num % sum == 0;
}
}
C-数组分割(✖️)
停留点:如何给R1,R2两个集合分配I的值??好像不能实现 先pass该题
D-矩形总面积
猜测:应该是简单的数学题分析
注意:数据的范围需要换成long
三种情况:
- 正常 有部分重叠
- R1+R2-重叠部分
- R1 和 R2 没有重叠区域
- R1+R2
- 其中一个矩形完全在另一个矩形内部
- R大-R小
思路:
-
找边界
- 左:min(x1,x3)
- 右:max(x2,x4)
- 上:min(y2,y4)
- 下:max(y1,y3)
-
找重叠部分(⭐难点 需要想清楚)(不一定存在)
- 高:(上-下)max(y2,y4)-min(y1,y3)
- 长:(右-左)max(x2,x4)-min(x1,x3)
代码实现:
import java.util.Scanner;
public class Main {
public static void main(String[] args) {
Scanner sc = new Scanner(System.in);
long x1 = sc.nextLong();
long y1 = sc.nextLong();
long x2 = sc.nextLong();
long y2 = sc.nextLong();
long x3 = sc.nextLong();
long y3 = sc.nextLong();
long x4 = sc.nextLong();
long y4 = sc.nextLong();
// 计算各自两个矩形单独面积
long R1 = (x2 - x1) * (y2 - y1);
long R2 = (x4 - x3) * (y4 - y3);
// 计算重叠区域的坐标
long left = Math.max(x1, x3);
long right = Math.min(x2, x4);
long bottom = Math.max(y1, y3);
long top = Math.min(y2, y4);
// 重叠区域的面积
long overlapArea = Math.max(0, right - left) * Math.max(0, top - bottom);
// 总面积是两个矩形的面积之和减去重叠区域的面积
long totalArea = R1 + R2 - overlapArea;
// 检查是否有矩形完全在另一个矩形内部
if (x1 >= x3 && x2 <= x4 && y1 >= y3 && y2 <= y4) {
// R1 完全在 R2 内部
totalArea = R2;
} else if (x3 >= x1 && x4 <= x2 && y3 >= y1 && y4 <= y2) {
// R2 完全在 R1 内部
totalArea = R1;
}
// 输出结果
System.out.println(totalArea);
sc.close();
}
}
G-买二赠一
理解了题意应该就不是特别难的吧…(失算了)
- 理解:最大程度地占便宜
- 思路:
- (毕竟有关价格)先对商品价格进行排序
- 借助指针,从最贵的两个买起,记录两者中较便宜的价格为P;买了这两个接下来就是享受优惠,找到价格等于或者小于P/2(最接近)的商品,获得它;同时要想办法标志知道这三件商品已经被拿下(可以标记价格为0);接着移动指针往低价方向走,同理判断获得;直到所有商品都被拿下(其中可能包括单独购买的)
代码:(通过率40% 并包括部分超时问题)
import java.util.Arrays;
import java.util.Scanner;
public class Main {
public static void main(String[] args) {
Scanner scanner = new Scanner(System.in);
int N = scanner.nextInt();
int[] a = new int[N];
for (int j = 0; j < N; j++) {
a[j] = scanner.nextInt();
}
Arrays.sort(a);
int money = 0;
int m = 0, n = 0; // 最贵的两个物品
int remain = 0; // 剩余未选择的物品数量
int i = N - 1; // 从数组末尾开始遍历
while (i >= 2) { // 确保有至少三个物品
m = a[i--]; // 选择最贵的物品
n = a[i--]; // 选择次贵的物品
money += m + n; // 加到总花费中
int p = m < n ? m : n; // 找到价值较小的物品
p /= 2; // 计算不超过这个价值一半的最大值
// 从后往前遍历,找到第一个不超过p的物品
boolean found = false;
for (int j = i; j >= 0; j--) { // 从后往前遍历
if (a[j] <= p && a[j] != 0) {
a[j] = 0; // 将物品标记为已选择
found = true;
break;
}
}
// 检查是否还有未选择的物品
remain = 0;
for (int j = 0; j <= i; j++) {
if (a[j] != 0) {
remain++;
}
}
if (remain <= 2) {
break; // 如果只剩下两个或更少的物品,结束循环
}
}
// 将剩余未选择的物品加到总花费中
for (int j = 0; j <= i; j++) {
if (a[j] != 0) {
money += a[j];
}
}
System.out.println(money);
}
}