Java-2023第十四届蓝桥杯Java B组题解

试题A :阶乘求和

题目

问题描述
令 S = 1! + 2! + 3! + … + 202320232023!,求 S 的末尾 9 位数字。
提示:答案首位不为 0。
答案提交
这是一道结果填空的题,你只需要算出结果后提交即可。本题的结果为一个整数,在提交答案时只填写这个整数,填写多余的内容将无法得分。

分析

这道题肯定不能按题目的要求算到 202320232023!,肯定有规律可循,所以不妨先算前50项,取最后9位看看结果。阶乘算出来的结果很大,用Long也会爆,可以用BigIneger,需要注意的是BigInteger的加减乘除需要调API实现。

代码实现

import java.math.BigInteger;

public class 试题A_阶乘求和 {

	public static void main(String[] args) {
		BigInteger n=new BigInteger("1");
		BigInteger sum=new BigInteger("0");
		for(int i=1;i<50;i++) {
			n=n.multiply(new BigInteger(""+i));
			sum=sum.add(n);
			System.out.println("i="+i+"时,sum后九位是:"+sum.mod(new BigInteger(""+1000000000)));
		}
	}
}

结果

结果可以发现,从第39项开始,最后9位数字就没有变化了,所以答案就是420940313

试题B:幸运数字

题目

问题描述
哈沙德数是指在某个固定的进位制当中,可以被各位数字之和整除的正整数。例如 126 是十进制下的一个哈沙德数,因为 (126)10 mod (1+2+6) = 0;126也是八进制下的哈沙德数,因为 (126)10 = (176)8,(126)10 mod (1 + 7 + 6) = 0;同时 126 也是 16 进制下的哈沙德数,因为 (126)10 = (7e)16,(126)10 mod (7 + e) = 0。小蓝认为,如果一个整数在二进制、八进制、十进制、十六进制下均为哈沙德数,那么这个数字就是幸运数字,第 1 至第 10 个幸运数字的十进制表示为:1 , 2 , 4 , 6 , 8 , 40 , 48 , 72 , 120 , 126 . . . 。现在他想知道第 2023 个幸运数字是多少?你只需要告诉小蓝这个整数的十进制表示即可。
答案提交
这是一道结果填空的题,你只需要算出结果后提交即可。本题的结果为一个整数,在提交答案时只填写这个整数,填写多余的内容将无法得分。

分析

这道题目单纯考察进制转换,调用Java现成的API即可:
十进制转二进制:Integer.toBinaryString(i)
十进制转八进制:Integer.toOctalString(i)
十进制转十六进制:Integer.toHexString(i)
💥💥求数位之和要注意:十六进制字符串可能包含a-f,所以需要特殊处理。

代码实现

public class 试题B_幸运数字 {
	//求一个数字字符串的数位之和
	public static int getSum(String s) {
		int sum=0;
		for(int i=0;i<s.length();i++) {
			if(s.charAt(i)>='a'&&s.charAt(i)<='f') //如果是a-f
				sum+=s.charAt(i)-'a'+10;
			else //如果是数字
				sum+=s.charAt(i)-'0';
		}
		return sum;
	}
	public static void main(String[] args) {
		int count=0;
		for(int i=1;i<1000000;i++) {
			String a2=Integer.toBinaryString(i);	//转为二进制
			String a8=Integer.toOctalString(i);		//转为八进制
			String a16=Integer.toHexString(i);		//转为十六进制
			if(i%getSum(a2)==0&&i%getSum(a8)==0&&i%getSum(a16)==0&&i%getSum(""+i)==0) {
				count++;
				if(count==2023) {
					System.out.println("第 2023 个幸运数字是:"+i);
					break;
				}
			}	
		}
	}
}


结果

第 2023 个幸运数字是:215040

试题C:数组分割

题目

问题描述
小蓝有一个长度为 N 的数组 A = [A0, A1, . . .,AN−1]。现在小蓝想要从 A 对应的数组下标所构成的集合 I = {0, 1, 2, . . . , N − 1} 中找出一个子集 R1,那么 R1在 I 中的补集为 R2。记 S1 = ∑ r ∈ R1 Ar,S2 = ∑ r ∈ R2 Ar,我们要求 S1 和 S2 均为偶数,请问在这种情况下共有多少种不同的 R1。当 R1 或 R2 为空集时我们将S 1 或 S 2 视为 0。
输入格式
第一行一个整数 T,表示有 T 组数据。
接下来输入 T 组数据,每组数据包含两行:第一行一个整数 N,表示数组A 的长度;第二行输入 N 个整数从左至右依次为 A0, A1, . . . , AN−1,相邻元素之间用空格分隔。
输出格式
对于每组数据,输出一行,包含一个整数表示答案,答案可能会很大,你需要将答案对 1000000007 进行取模后输出。
样例输入
2
2
6 6
2
1 6
样例输出
4
0
样例说明
对于第一组数据,答案为 4。(注意:大括号内的数字表示元素在数组中的下标。)
R1 = {0}, R2 = {1};此时 S 1 = A0 = 6 为偶数, S 2 = A1 = 6 为偶数。
R1 = {1}, R2 = {0};此时 S 1 = A1 = 6 为偶数, S 2 = A0 = 6 为偶数。
R1 = {0, 1}, R2 = {};此时 S 1 = A0 + A1 = 12 为偶数, S 2 = 0 为偶数。
R1 = {}, R2 = {0, 1};此时 S 1 = 0 为偶数, S 2 = A0 + A1 = 12 为偶数。
对于第二组数据,无论怎么选择,都不满足条件,所以答案为 0。
评测用例规模与约定
对于 20% 的评测用例,1 ≤ N ≤ 10。
对于 40% 的评测用例,1 ≤ N ≤ 102。
对于 100% 的评测用例,1 ≤ T ≤ 10, 1 ≤ N ≤ 103, 0 ≤ Ai ≤ 109。

分析

这道题我想到的是暴力解法。对于所给数组A(元素可重复),用回溯法求解出其所有的子集R1,将数组A的元素和 - 子集R1的元素和,即得到补集R2的元素和,然后判断是否满足条件。
💥💥 注意:将答案对 1000000007 进行取模后输出

代码实现

import java.util.ArrayList;
import java.util.Arrays;
import java.util.LinkedList;
import java.util.List;
import java.util.Scanner;
import java.util.stream.Collectors;

// 子集类
class SubSet{
	List<List<Integer>> res=new LinkedList<List<Integer>>();//存储所有子集
	LinkedList<Integer> track=new LinkedList<Integer>();	//存储一个子集
	boolean[] used;			//记录是否访问过
	// 求出所有子集
	List<List<Integer>> subset(int[] nums){
		used=new boolean[nums.length];
		backtrack(nums, 0);
		return res;
	}
	// 回溯法求子集
	void backtrack(int[] nums,int start) {
		res.add(new LinkedList<Integer>(track));
		//回溯框架
		for(int i=start;i<nums.length;i++) {
			//不用剪枝,可以重复
//			if(i>start&&nums[i]==nums[i-1]&&!used[i-1]))
//				continue;
			//选择
			used[i]=true;
			track.add(nums[i]);
			//进入下一层回溯
			backtrack(nums, i+1);
			//撤销选择
			track.removeLast();
			used[i]=false;
		}
		
	}
}
public class 试题C_数组分割 {
	//返回一个列表的所有数字的和
	public static int getSum(List<Integer> nums) {
		int sum=0;
		for(int e:nums)
			sum+=e;
		return sum;
	}
	//统计满足条件的R1个数
	public static int count(int[] A) {
		SubSet subSet=new SubSet();
		List<List<Integer>> resList=subSet.subset(A);
		int count=0;	//统计满足情况的R1个数
		for(List<Integer> R1:resList) {
			//将数组A转为List
			List<Integer> listA=Arrays.stream(A).boxed().collect(Collectors.toList());
			//R1的补集R2的元素和S2
			int S2=(getSum(listA)-getSum(R1))%2;
			if(getSum(R1)%2==0&&S2==0)
				count++;
		}
		return count%1000000007;
	}
	public static void main(String[] args) {
		//输入
		Scanner sc=new Scanner(System.in);
		int T=sc.nextInt();		//有T组数据
		int[] res=new int[T];	//记录每组数据的结果
		for(int i=0;i<T;i++) {
			int N=sc.nextInt();
			int[] A=new int[N];	//存放每组数据
			for(int j=0;j<N;j++) {
				A[j]=sc.nextInt();
			}
			res[i]=count(A);
		}
		for(int i=0;i<T;i++) {
			System.out.println(res[i]);
		}
	}
}

  • 2
    点赞
  • 17
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值