第四届蓝桥杯JAVA组C组国赛题解

闲话不多说,直接上代码!
1.好好学习
(1)题目描述

    汤姆跟爷爷来中国旅游。一天,他帮助中国的小朋友贴标语。他负责贴的标语是分别写在四块红纸上的四个大字:“好、好、学、习”。但是汤姆不认识汉字,他就想胡乱地贴成一行。

    请你替小汤姆算一下,他这样乱贴,恰好贴对的概率是多少?

    答案是一个分数,请表示为两个整数比值的形式。例如:1/3 或 2/15 等。
如果能够约分,请输出约分后的结果。

    注意:不要书写多余的空格。


    请严格按照格式,通过浏览器提交答案。
    注意:只提交这个比值,不要写其它附加内容,比如:说明性的文字。

(2)涉及知识点:全排列+去重
(3)分析与解答:这道题目没啥难的,全排列可以得到所有的结果,但是由于好字重复了,所以要加入Set去重
(4)代码:

import java.util.HashSet;

public class Main04JC01 {

	private static HashSet<String> set=new HashSet<>();

	public static void main(String[] args) {
		// TODO Auto-generated method stub
		char a[]={1,1,2,3};
		dfs(a,0);
		System.out.println("1/"+set.size()/2);
	}

	private static void dfs(char[] a, int k) {
		// TODO Auto-generated method stub
		if(k==4){
			set.add(new String(a));
		}
		for(int i=k;i<4;i++){
			char temp=a[i];
			a[i]=a[k];
			a[k]=temp;
			dfs(a,k+1);
			temp=a[i];
			a[i]=a[k];
			a[k]=temp;
		}
	}

}

2.埃及分数
(1) 题目描述

    古埃及曾经创造出灿烂的人类文明,他们的分数表示却很令人不解。古埃及喜欢把一个分数分解为类似: 1/a + 1/b 的格式。

    这里,a 和 b 必须是不同的两个整数,分子必须为 1

    比如,2/15 一共有 4 种不同的分解法(姑且称为埃及分解法):

	1/8 + 1/120
	1/9 + 1/45
 	1/10 + 1/30
	1/12 + 1/20


    那么, 2/45 一共有多少个不同的埃及分解呢(满足加法交换律的算同种分解)? 请直接提交该整数(千万不要提交详细的分解式!)。

    请严格按照要求,通过浏览器提交答案。
    注意:只提交分解的种类数,不要写其它附加内容,比如:说明性的文字

(2)涉及知识点:双精度运算+暴力枚举
(3)分析与解答:这道题乍看之下非常简单,但是国赛遇到这种题目千万长个心眼,后面可能暗藏着什么陷阱,其实你想想看也知道,国赛A组第三题,怎么可能出这种初学者都会做的题目,这道题目出得非常刁钻。刁钻在哪里,一开始我用的是(double)1/i+(double)1/j==(double)2/45,为了保险我还特地测了一下2/15的答案,结果2/15的答案还凑巧是对的,但是这道题目结果换成2/45就是错的,其实具体原因我也说不清楚,我唯一的理解就是double只有14位小数,运算算不到那么精准吧,当然我也不会误差排除,所以这里换个方法。怎么办呢?其实除是不精确的,但是乘法是一定精确的,所以在个人建议能用乘法尽量不要用除法,所以这里要做的是同分,转换成乘法45j+45i=2ij,其实这样double类型都不需要了,int型就可以了。
(4)代码:

public class Main04JC02 {

	private static int ans;

	/**
	 * @param args
	 */
	public static void main(String[] args) {
		// TODO Auto-generated method stub
		for(double i=1;i<=10000;i++){
			for(double j=i+1;j<=10000;j++){
				if((double)45*j+45*i==(double)2*i*j){
					System.out.println(i+" "+j);
					ans++;
				}
			}
		}
		System.out.println(ans);
	}

}

3.金蝉素数
(1)题目描述

    考古发现某古墓石碑上刻着一个数字:13597,后研究发现:

    这是一个素数!
    
    并且,去掉首尾数字仍是素数!

    并且,最中间的数字也是素数!

    这样特征的数字还有哪些呢?通过以下程序的帮助可以轻松解决。请仔细阅读代码,并填写划线部分缺失的代码。

public class A
{
	static boolean isPrime(int n)
	{
		if(n<=1) return false;
		for(int i=2; i*i<=n; i++){
			if(n%i==0) return false;
		}
		return true;
	}
	
	static void f(int[] x, int k)
	{
		if(_____________________________){  // 填空位置
			if(isPrime(x[0]*10000 + x[1]*1000 + x[2]*100 + x[3]*10 + x[4]) &&
				isPrime(x[1]*100 + x[2]*10 + x[3]) &&
				isPrime(x[2]))
				System.out.println(""+x[0]+x[1]+x[2]+x[3]+x[4]);
			return;
		}
		
		for(int i=k; i<x.length; i++){
			{int tmp=x[k]; x[k]=x[i]; x[i]=tmp; }
			f(x,k+1);
			{int tmp=x[k]; x[k]=x[i]; x[i]=tmp; }
		}
	}
	
	static void test()
	{
		int[] x = {1,3,5,7,9};
		f(x,0);
	}
	
	public static void main(String[] args)
	{
		test();
	}
}


    请分析代码逻辑,并推测划线处的代码,通过网页提交。
    注意:仅把缺少的代码作为答案,千万不要填写多余的代码、符号或说明文字!!

(2)涉及知识点:全排列
(3)分析与解答:基本上看到for循环的交换就知道是全排列了,非常简单的一道题,这里就不多阐述了。
(4)代码:

public class Main04JC03 {
	static boolean isPrime(int n)
	{
		if(n<=1) return false;
		for(int i=2; i*i<=n; i++){
			if(n%i==0) return false;
		}
		return true;
	}
	
	static void f(int[] x, int k)
	{
		if(k==5){  // 填空位置
			if(isPrime(x[0]*10000 + x[1]*1000 + x[2]*100 + x[3]*10 + x[4]) &&
				isPrime(x[1]*100 + x[2]*10 + x[3]) &&
				isPrime(x[2]))
				System.out.println(""+x[0]+x[1]+x[2]+x[3]+x[4]);
			return;
		}
		
		for(int i=k; i<x.length; i++){
			{int tmp=x[k]; x[k]=x[i]; x[i]=tmp; }
			f(x,k+1);
			{int tmp=x[k]; x[k]=x[i]; x[i]=tmp; }
		}
	}
	
	static void test()
	{
		int[] x = {1,3,5,7,9};
		f(x,0);
	}
	
	public static void main(String[] args)
	{
		test();
	}
}

4.横向打印二叉树

(1)题目描述

    二叉树可以用于排序。其原理很简单:对于一个排序二叉树添加新节点时,先与根节点比较,若小则交给左子树继续处理,否则交给右子树。

    当遇到空子树时,则把该节点放入那个位置。 

    比如,10 8 5 7 12 4 的输入顺序,应该建成二叉树如图1所示。 

    本题目要求:根据已知的数字,建立排序二叉树,并在标准输出中横向打印该二叉树。 

    输入数据为一行空格分开的N个整数。 N<100,每个数字不超过10000。
    输入数据中没有重复的数字。 

    输出该排序二叉树的横向表示。 对应上例中的数据,应输出:

   |-12
10-|
   |-8-|
       |   |-7
       |-5-|
           |-4


    为了便于评卷程序比对空格的数目,请把空格用句点代替:
...|-12
10-|
...|-8-|
.......|...|-7
.......|-5-|
...........|-4

例如:
用户输入:
10 5 20
则程序输出:
...|-20
10-|
...|-5

再例如:
用户输入:
5 10 20 8 4 7
则程序输出:
.......|-20
..|-10-|
..|....|-8-|
..|........|-7
5-|
..|-4


资源约定:
峰值内存消耗(含虚拟机) < 64M
CPU消耗  < 1000ms


请严格按要求输出,不要画蛇添足地打印类似:“请您输入...” 的多余内容。

所有代码放在同一个源文件中,调试通过后,拷贝提交该源码。
注意:不要使用package语句。不要使用jdk1.6及以上版本的特性。
注意:主类的名字必须是:Main,否则按无效代码处理。

(2)涉及知识点:排序二叉树构建+图形输出
(3)分析与解答:这道题目我一看虽然有思路但我不太想做,因为这道题目的输入和输出能把人搞吐血了,国赛遇到这种题目是很考验一个人的耐心的,辛苦半天还不见得做得对,这里就偷个懒不做了,附上被人的代码。
(4)代码:

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.util.ArrayList;
import java.util.Collections;
import java.util.LinkedList;
import java.util.Scanner;
 
class Node
{
	int n;
	int l = 100000,r = 100000;
}
 
public class Main04JC04
{
	static Node tree[];
	static int lev[];
	static int n;
	static String x[];
	static int rec[];
	static int ans = 0;
	public static void main(String [] args) throws IOException
	{
		Scanner sc = new Scanner(System.in);
		BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
		String str = br.readLine();
		String[] t = str.split(" ");
		n = t.length;
		int arr[] = new int[n];
		tree = new Node[n];
		lev = new int[n];
		x = new String[n];
		rec = new int[n];
		for(int i = 0; i < n; ++i)
		{
			x[i] = "";
			arr[i] = Integer.valueOf(t[i]);
		}
		
		
		tree[0] = new Node();
		tree[0].n = arr[0];
		lev[0] = 0;
		for(int i = 1; i < n; ++i)
		{
			tree[i] = new Node();
			tree[i].n = arr[i];
			addNode(0,i,String.valueOf(tree[0].n).length()+1);
		}
		disp(0);
		fun2();
		fun();
		
		sc.close();
	}
	
	static void fun()
	{
		for(String temp : x)
		{
			System.out.println(temp);
		}
	}
	static void fun2()
	{
		for(int i = 0; i < n; ++i)
		{
			if(tree[i].r != 100000)
			{
				int t1 = rec[i];
				int t2 = x[t1].length();
				for(int j = 1; x[t1-j].charAt(t2-1) != '|'; ++j)
				{
					char t3[] = x[t1-j].toCharArray();
					t3[t2-1] = '|';
					x[t1-j] = String.valueOf(t3);
				}
			}
			if(tree[i].l != 100000)
			{
				int t1 = rec[i];
				int t2 = x[t1].length();
				for(int j = 1; x[t1+j].charAt(t2-1) != '|'; ++j)
				{
					char t3[] = x[t1+j].toCharArray();
					t3[t2-1] = '|';
					x[t1+j] = String.valueOf(t3);
				}
			}
		}
	}
	static void disp(int i)
	{
		if(i == 100000)
			return;
		disp(tree[i].r);
		for(int cnt = 0; cnt < lev[i]; ++cnt)
		{
			x[ans] += ".";
		}
		
		if(i == 0)
		{
			if(tree[i].r != 100000 || tree[i].l != 100000)
			{
				rec[i] = ans;
				x[ans++] += tree[i].n + "-|";
			}
				
			else
			{
				rec[i] = ans;
				x[ans++] += tree[i].n + "";
			}
				
		}
		else
		{
			if(tree[i].r != 100000 || tree[i].l != 100000)
			{
				rec[i] = ans;
				x[ans++] += "|-" + tree[i].n + "-|";
			}
			else
			{
				rec[i] = ans;
				x[ans++] += "|-" + tree[i].n;
			}
				
		}
			
		disp(tree[i].l);
		
	}
	
	static void addNode(int root,int i,int level)
	{
		if(tree[i].n < tree[root].n)
		{
			if(tree[root].l == 100000)
			{
				lev[i] = level;
				tree[root].l = i;
				return;
			}
				
			else
			{
				addNode(tree[root].l,i,3+level+String.valueOf(tree[tree[root].l].n).length());
				return;
			}
				
		}
		else
		{
			if(tree[root].r == 100000)
			{
				lev[i] = level;
				tree[root].r = i;
				return;
			}
				
			else
			{
				addNode(tree[root].r,i,3+level+String.valueOf(tree[tree[root].r].n).length());
				return;
			}
				
		}
		
	}
	
}

5.危险系数
(1)题目描述

  抗日战争时期,冀中平原的地道战曾发挥重要作用。

    地道的多个站点间有通道连接,形成了庞大的网络。但也有隐患,当敌人发现了某个站点后,其它站点间可能因此会失去联系。
  
    我们来定义一个危险系数DF(x,y):
    对于两个站点x和y (x != y), 如果能找到一个站点z,当z被敌人破坏后,x和y不连通,那么我们称z为关于x,y的关键点。相应的,对于任意一对站点x和y,危险系数DF(x,y)就表示为这两点之间的关键点个数。

    本题的任务是:已知网络结构,求两站点之间的危险系数。


    输入数据第一行包含2个整数n(2 <= n <= 1000), m(0 <= m <= 2000),分别代表站点数,通道数;
    接下来m行,每行两个整数 u,v (1 <= u, v <= n; u != v)代表一条通道;
    最后1行,两个数u,v,代表询问两点之间的危险系数DF(u, v)。

    输出:一个整数,如果询问的两点不连通则输出-1.


例如:
用户输入:
7 6
1 3
2 3
3 4
3 5
4 5
5 6
1 6
则程序应该输出:
2

资源约定:
峰值内存消耗(含虚拟机) < 64M
CPU消耗  < 2000ms


请严格按要求输出,不要画蛇添足地打印类似:“请您输入...” 的多余内容。

所有代码放在同一个源文件中,调试通过后,拷贝提交该源码。
注意:不要使用package语句。不要使用jdk1.6及以上版本的特性。
注意:主类的名字必须是:Main,否则按无效代码处理。

(2)涉及知识点:无向图邻接矩阵构建+dfs
(3)分析与解答:
(4)代码:

import java.util.Scanner;

public class Main04JC05 {
	static int n,m;
	static int map[][];//声明一个二维数组,记录站点与站点之间是否联通,连通为1,不连通为0;
    static int via[]=new int[2000];//这个数组是用来记录每次深搜所经过的站点
    static int site[];//这个数组用来记录经过每个站点的次数,有几个城市创建多大的数组
    static int h;//该变量用于via数组的下标,通过遍历via数组便能知道经过该站点多少次
    static int num;//记录从起点到终点一共有多少条路
    static int visited[];//深搜时用来记录该站点是否访问过

	public static void main(String[] args) {
		// TODO Auto-generated method stub
		Scanner scan=new Scanner(System.in);
		n=scan.nextInt();
		m=scan.nextInt();
		map=new int[n+1][n+1];
		site=new int[n+1];
		visited=new int[n+1];
		for(int i=0;i<m;i++) {
			int x =scan.nextInt();
			int y =scan.nextInt();
			//代表两站点之间有通道
			map[x][y]=1;
			map[y][x]=1;
		}
		int start =scan.nextInt();
		int end =scan.nextInt();
		dfs(start,end);

		int sum=0;
		for(int i=1;i<=n;i++) {
			if(site[i]==num) {
				sum++;
			}
		}
		System.out.println(sum-1);

	}
	private static void dfs(int start, int end) {
		// TODO Auto-generated method stub
		if(start==end){
			num++;
			for(int i=0;i<h;i++){
				site[via[i]]++;
			}
		}else{ 
			for(int i=1;i<=n;i++){
			if(visited[i]!=1&&map[start][i]==1){
				visited[i]=1;
				System.out.println("h:"+h+" i:"+i);
				via[h++]=i;
				dfs(i,end);
				visited[i]=0;
				h--;
			}
		}
		}
	}

}

6.公式求值
(1)题目描述

    输入n, m, k,输出图1所示的公式的值。其中C_n^m是组合数,表示在n个人的集合中选出m个人组成一个集合的方案数。组合数的计算公式如图2所示。

    输入的第一行包含一个整数n;第二行包含一个整数m,第三行包含一个整数k。

    计算图1所示的公式的值,由于答案非常大,请输出这个值除以999101的余数。

【样例输入1】
3
1
3
【样例输出1】
162

【样例输入2】
20
10
10
【样例输出2】
359316

【数据规模与约定】
对于10%的数据,n≤10,k≤3;
对于20%的数据,n≤20,k≤3;
对于30%的数据,n≤1000,k≤5;
对于40%的数据,n≤10^7,k≤10;
对于60%的数据,n≤10^15,k ≤100;
对于70%的数据,n≤10^100,k≤200;
对于80%的数据,n≤10^500,k ≤500;
对于100%的数据,n在十进制下不超过1000位,即1≤n<10^1000,1≤k≤1000,同时0≤m≤n,k≤n。

【提示】
999101是一个质数;
当n位数比较多时,绝大多数情况下答案都是0,但评测的时候会选取一些答案不是0的数据;

资源约定:
峰值内存消耗(含虚拟机) < 128M
CPU消耗  < 2000ms


请严格按要求输出,不要画蛇添足地打印类似:“请您输入...” 的多余内容。

所有代码放在同一个源文件中,调试通过后,拷贝提交该源码。
注意:不要使用package语句。不要使用jdk1.6及以上版本的特性。
注意:主类的名字必须是:Main,否则按无效代码处理。

(2)涉及知识点:数论相关知识
(3)分析与解答:这道题我一时半会儿看不懂,毕竟数论没怎么学,附上大佬代码(90%的通过率)
https://blog.csdn.net/u010836847/article/details/21166725?utm_source=copy
(4)代码:

import java.math.BigInteger;
import java.util.Scanner;
 
public class Main04JC06 {
	/***
	 * @author 林梵
	 */
	public static BigInteger lucas(BigInteger n,BigInteger m,BigInteger p){
		if(m.equals(BigInteger.ZERO)) return BigInteger.ONE;
		return BigInteger.valueOf(f(n.mod(p).longValue(),m.mod(p).longValue())).multiply(lucas(n.divide(p),m.divide(p),p)).mod(p);
	}
 
	public static long f(long n,long m){
		if(m>n) return 1;
		if(n==m|| m==0) return 1;
		if(m>n-m) m=n-m;
		long tmpi=1,tmpn=1,s1=1,s2=1,ans=1;
		for (int i = 1; i<=m; i++) {
			tmpi=i;
			tmpn=n-i+1;
			s1=s1*tmpi%999101;
			s2=s2*tmpn%999101;
		}
		ans = s2*pow1(s1,999099)%999101;
		return ans%999101;
	}
	public static long pow1(long x,long n) {
		if(x==1) return 1;
		if (n==0)
			return 1;
		else {
			while ((n & 1)==0) {
				n>>=1;
				x=(x *x)%999101;
			}
		}
		long result = x%999101;
		n>>=1;
		while (n!=0) {
			x=(x *x)%999101;;
			if ((n & 1)!=0)
				result =result*x%999101;
			n>>=1;
		}
		return  result;
	}
	public static void main(String[] args) {
		Scanner sc = new Scanner(System.in);
		BigInteger n = new BigInteger(sc.nextLine());
		BigInteger m = new BigInteger(sc.nextLine());
		int k = Integer.parseInt(sc.nextLine());
		long start = System.currentTimeMillis();
		BigInteger md = new BigInteger("999101");
		long Cnm=lucas(n, m,md).longValue()%999101;
		long sum = 0;
		if(Cnm!=0){
			int[][] a = new int[k][k];
			int h = 1;
			for (int i = 0; i < k; i++) {
				for (int j = 0; j < k; j++) {
					if (j >= h)
						a[i][j] =0;
					else {
						if (j == 0 || j == h - 1)
							a[i][j] = 1;
						else {
							a[i][j] = (a[i - 1][j - 1]*(h - j)+a[i - 1][j])%999101;
						}
					}
				}
				h++;
			}
			long m1 = 1,n1 =1;
			long x=n.subtract(new BigInteger(k+"")).mod(md.subtract(BigInteger.ONE)).longValue();
			long n3 = pow1(2,x);
			for (int i = k - 1; i >= 0; i--) {
				n1=n3*pow1(2,i)%999101;
				m1 = m1*(n.subtract(new BigInteger((k - 1 - i) + "")).mod(md).longValue())%999101;
				sum = (sum+m1*a[k - 1][i]*n1)%999101;
			}
			sum = sum*Cnm%999101;
		}
		System.out.println(sum);
		long end = System.currentTimeMillis();
		System.out.println(end - start);
	}
 
}
  • 1
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值