第十二届蓝桥杯 Java 省赛 B 组部分真题解析

第一题:Char(送分题)

  char类型以ASCAII值存储,1-9(48~57)、A~Z(65~90)、a-z(97~122)


第二题:卡牌(送分题,一个顺序循环就可以出来了)

package Test;

public class Main {

	public static void main(String[] args) {
		int a=0,b=0,c=0,d=0,e=0,f=0,g=0,l=0,m=0,n=0;
		int sum=1;
		while(true) {
			String k = String.valueOf(sum);
			char[] p=k.toCharArray();
			int size=p.length;
			if(k.contains("0")) {for(int t=0;t<size;t++) {if(p[t]=='0') a++;}}
			if(k.contains("1")) {for(int t=0;t<size;t++) {if(p[t]=='1') b++;}}
			if(k.contains("2")) {for(int t=0;t<size;t++) {if(p[t]=='2') c++;}}
			if(k.contains("3")) {for(int t=0;t<size;t++) {if(p[t]=='3') d++;}}
			if(k.contains("4")) {for(int t=0;t<size;t++) {if(p[t]=='4') e++;}}
			if(k.contains("5")) {for(int t=0;t<size;t++) {if(p[t]=='5') f++;}}
			if(k.contains("6")) {for(int t=0;t<size;t++) {if(p[t]=='6') g++;}}
			if(k.contains("7")) {for(int t=0;t<size;t++) {if(p[t]=='7') l++;}}
			if(k.contains("8")) {for(int t=0;t<size;t++) {if(p[t]=='8') m++;}}
			if(k.contains("9")) {for(int t=0;t<size;t++) {if(p[t]=='9') n++;}}
			if(a>2021||b>2021||c>2021||d>2021||e>2021||f>2021||g>2021||l>2021||m>2021||n>2021)
            break;
			sum++;}
		System.out.print(sum-1);

	}

}

第三题:直线(需要仔细处理ABC的成倍数出现的情况)


思路:先用一个循环把所有的点的坐标放在一个数组里面,我这里用的List(好处是不用设定大小)。然后对list里面的每一个值循环访问其他值(即取出两个不同点坐标(x1,y1)(x2,y2)),通过直线的一般式Ax+By+C=0得到A=y2-y1,B=x1-x2,C=x2*y1-x1*y2,对ABC的组合消除倍数关系加入到<String>泛型集合中,因为set的自动去重只对基本类(比如String,Integer等)有效,然后输出set.size().

import java.util.*;

public class X {
	public static void main(String[] args) {
		
		Set<String> set=new HashSet();
		List<String> list = new ArrayList<String>();
		for(int i=0;i<20;i++) {
			for(int j=0;j<21;j++) {				
				list.add(String.valueOf(i)+"|"+String.valueOf(j));
			}
		}
		
		for(int i=0;i<list.size();i++) {
			for(int j=0;j<list.size();j++) {
				
				String st1=list.get(i);
				String st2=list.get(j);
				int x1 = Integer.parseInt(st1.substring(0,st1.indexOf("|")));
				int y1 = Integer.parseInt(st1.substring(st1.indexOf("|")+1));
				int x2 = Integer.parseInt(st2.substring(0,st2.indexOf("|")));
				int y2 = Integer.parseInt(st2.substring(st2.indexOf("|")+1));
				int A=y2-y1;
				int B=x1-x2;
				int C=x2*y1-x1*y2;
				
				//去除整数倍的情况
				if(A==0&&C==0) {B=1;}
				if(B==0&&C==0) {A=1;}
				if(A==0&&B<0)  {A=-A;B=-B;C=-C;}
				if(A<0) {A=-A;B=-B;C=-C;}
				while(true) {
					boolean pd=false;
					int Max = Math.max(Math.max(A, Math.abs(B)),Math.abs(C));
					for(int l=2;l<=Max;l++) {
						if(A%l==0&&B%l==0&&C%l==0) {A/=l;B/=l;C/=l;pd=true;}
					}
					if(pd==false) break;
					}
				
				set.add(String.valueOf(A)+"|"+String.valueOf(B)+"|"+String.valueOf(C));
				
				
				
			}
		}
	System.out.print(set.size());

		
	}

 
}

第四题:因子的排列组合

这里看起来好像很简单:两个for循环从1到N,如果N%(i*j)==0 sum++的思路不是不可以,但是复杂度太高了,对于一个长整型的数据,跑一遍for需要好久,何况两遍?于是这题我和电脑对着坐牢,几个小时都没结果的那种。看有些人的处理是从质数因子组合去写,额...即任何一个数都可以拆分成质因子的组合,然后排列组合,算了,跳过吧,我宁愿坐牢


第五题:最短路(最简单的情况,直接Floyd)

思路:对于这2021个点我们建立一个2022*2022的二维数组作为带权的邻接矩阵,权值为lcm()

public class Floyd {
	public static void main(String[] args) {
		int[][] Linjie =new int[2022][2022];
		for(int i=1;i<=2021;i++) {
			for(int j=i+1;j<=Math.min(2021-i,21)+i;j++) {
				Linjie[i][j]=Linjie[j][i]=lcm(j,i);
			}
		}
		for(int i=1;i<=2021;i++) {
			for(int j=1;j<=2021;j++) {
				if(Linjie[i][j]==0) continue;
				for(int k=1;k<=2021;k++) {
					if(Linjie[k][i]==0) continue;
					else if(Linjie[j][k]==0||Linjie[j][k]>Linjie[i][j]+Linjie[k][i]) Linjie[j][k]=Linjie[i][j]+Linjie[k][i];
				}
			}
		}
		System.out.print(Linjie[1][2021]);
	}

	private static int lcm(int j, int i) {
		return (i*j)/gcd(j,i);
		
	}

	private static int gcd(int j, int i) {
		
		return i==0?j:gcd(i,j%i);
	}
}

第六题:时间转换(送分题,注意是ms,使用前先除以1000,一天86400s,一个小时3600s,一分钟60s,知道这些就可以套用/和%的组合得出结果了)

import java.util.Scanner;

public class Time {

	public static void main(String[] args) {
		Scanner time =new Scanner(System.in);
		long t= time.nextLong()/1000;
		long duo=t%86400;
		long H =duo/3600;
		long M =duo%3600/60;
		long S =duo%3600%60;
		String h=String.valueOf(H);
		String m=String.valueOf(M);
		String s=String.valueOf(S);
		if(H<10) h="0"+h;
		if(M<10) m="0"+m;
		if(S<10) s="0"+s;
		System.out.print(h+":"+m+":"+s);
		

	}

}

第七题:砝码

    找规律题目,规律就是从1开始,每加一次砝码所表示的最大范围为3*n+1,数列左开右闭:为1  4  13  40 121...也就是说在2~4<1 3>之间用2个就可以表示,5~13之间用3个<1 3 9>就可以表示 ,14~40用4个表示<1 3 9 27> ,那么3*n+1怎么推出来的呢,其实也不是很难想出来,当N=1的时候 用一个砝码,重量为1,当N=2的时候,用两个砝码,重量为1和2,但是我想N=4的时候重量为1和4就表示不出来2,但是取1和3就可以连续的表示出1-4中的每一种数,于是3这个数就定下来了,定下来这两个砝码的重量后我发现现在我可以表示1-4的任何数据,依此递推到N=5时我无法表示,所以我只能继续加砝码,为了扩大表示范围,我将第三个砝码的重量定为9.这里说一下,为什么是9而不能是8或者10或者其他的呢。因为我们以前拥有的两个砝码已经可以表示1-4,如果我们将1-4作为下一个砝码的减法,那么这个砝码最大不超过9,因为如果第三个砝码重为10,那么10-4=6(最多只能减到6),无法表示出5,于是第三个砝码的重量定为9,因为9可以加上或者减去前面的范围值,那么所表示的范围也从1-4变为1-13,依次类推,这个范围变成了下个砝码的减法,于是这个砝码的重量为13+13+1=27,所表示的范围为13+13+1+13=40,这个更大的范围(1-40)其实是前一个范围(1-13)的三倍多1。

import java.util.*;
public class M {
	public static void main(String[] args) {
		Scanner sc =new Scanner(System.in);
		int N =sc.nextInt();
		int sum=1,num=1;
		while(true) {
			if(num>=N) break;
			num=num*3+1;
			sum++;
			
		}
		System.out.print(sum);
		
	}
}

第八题:杨辉三角(送分题)

真不是我说第7题需要想一想规律,前三个编程题目就有两个送的?



import java.util.Scanner;

public class Yanghui {
    static int[][] k = new int[100][100];
	public static void main(String[] args) {
	//杨辉三角本来是金字塔形,但是为了方便我们把他变成半三角形(直角在左下角),根据规律第一列全为1,斜边也全是1,其他的数据k[i][j]等于k[i-1][j]+k[i-1][j-1]
        Scanner r =new Scanner(System.in);
        int N =r.nextInt();
		
		for(int i=0;i<100;i++) {
			for(int j=0;j<=i;j++) {
				k[i][j]=1;
			}
		}
		
		for(int i=2;i<100;i++) {
			for(int j=1;j<i;j++) {
				k[i][j]=k[i-1][j]+k[i-1][j-1];
			}
			
		}
		
		System.out.print(getN(N));
		
		
	}

	static int getN(int N) {
		int num=0;
		for(int i=0;i<100;i++) {
			for(int j=0;j<=i;j++) {
				num++;
				if(k[i][j]==N) return num;
			}
			
		}
		return -1;
	}
}

第九题:排列数组

这种题目无脑暴力解决,得到一部分分数真的太简单


import java.util.*;

public class Xu {

	public static void main(String[] args) {
	
		Scanner s = new Scanner(System.in);
		int n=s.nextInt();                        //一共N个数字1-n
		int m=s.nextInt();                        //操作次数  
		int[] N =new int[n+1];
		for(int i=1;i<=n;i++) {
			N[i]=i;
		}
		int[][] k = new int[m+1][2];
		
		for(int i=1;i<=m;i++) {
			for(int j=0;j<=1;j++) {
				k[i][j]=s.nextInt();
			}
		}
	
		for(int i=1;i<=m;i++) {
			if(k[i][0]==0) {
				
				for(int j=1;j<=k[i][1];j++) {
					int max=N[j];
					for(int l=j+1;l<=k[i][1];l++) {
						if(N[l]>max) {
							max=N[l];
							int p=N[j];
							N[j]=N[l];
							N[l]=p;
						}
					}
				}
				
				
			}
			else {
				for(int j=k[i][1];j<=n;j++) {
					int min=N[j];
					for(int l=j+1;l<=n;l++) {
						if(N[l]<min) {
							min=N[l];
							int p=N[j];
							N[j]=N[l];
							N[l]=p;
						}
					}
				}
				
				
			}
		}
		for(int i=1;i<N.length;i++) {
			System.out.print(N[i]+" ");
		}
		
		
		

	}

}

第十题:括号纠正

我只能说编程不复杂但是很费时间,因为要处理的情况有点多。首先对字符串里面的"("和")"的数量做提取,如果左括号多于右括号,多几个就要插几个右括号(还是暴力~嵌套n重循环,利用递归去嵌套,因为n是变量,他根据括号差来嵌套的),同理,如果右括号多了,多几个右括号就要插入几个左括号,如果一样多,首先将这个字符串里面成对出现的“()”做一个删除直到没有“()”出现,比如原来的括号情况为)(())(,处理后为)(,对这种再进行处理,也是暴力~嵌套n重循环~递归嵌套,嵌套的层数为处理后的字符串的长度,n重循环对应着n个插入点去插入括号,利用递归的思想最后根据位置索引列表去插字符串,然后判断是否合格,就是对插入后的字符串去()化,直到字符串为空字符串(长度小于1)就是合格的字符串然后加入到set去重

这里需要注意一些:字符串的插入属于StringBuffer的操作,插入和删除都会对源字符串进行破坏,所以在操作之前可以new一个新的StringBuffer去操作,保留字符串的完整性

import java.util.*;
import java.util.Scanner;
import java.util.Set;

public class Kuohao {
	static String str="(";
	static Set<String> set = new HashSet();
	static List<Integer> list = new ArrayList<Integer>();
	public static void main(String[] args) {
		Scanner s = new Scanner(System.in);
		String n=s.next();
		char[] ch = n.toCharArray();
		int Left=0,Right=0,dif=0;
		for(int i=0;i<ch.length;i++) {
			if(ch[i]=='(') Left++;
			else Right++;
		}
		StringBuffer k = new StringBuffer(n);
		
		if(Left==Right) {
			StringBuffer k1 = new StringBuffer(k);
			Qukuohao(k1);
			dif=k1.length();
			for_n_Duplicate(dif,ch.length,k,0);
			for(String w:set) {
				System.out.print(w+" ");
			}
		}
		else{
			if(Left>Right) {dif=Left-Right;str=")";}
		    if(Left<Right)  {dif=Right-Left;}
		    
		  //因为括号差为N所以有N重嵌套循环,需要用到for递归
		    for_n(dif,ch.length,k,str,0);
		    System.out.println(set.size());
		    }
	}
	
	private static void Qukuohao(StringBuffer sb) {
		while(true) {
			
			if(sb.toString().contains("()"))
			{
					for(int k=0;k<sb.length()-1;k++) {
				        if(sb.charAt(k)=='('&&sb.charAt(k+1)==')') {sb.delete(k, k+2);break;}
			        }
					
			}
			else break;
		}
		
	}
	
	static boolean isTrue(StringBuffer k) {
		while(true){
			boolean b=false;
		for(int i=0;i<k.length()-1;i++) {
			if(k.charAt(i)=='(' && k.charAt(i+1)==')') { k.delete(i, i+2);b=true;break;}
			
		}
		if(b==false) break;
		
		}
		if(k.length()<1) return true;
		else return false;
	}
	
	//N重循环处理(有两种思路:第一种就是每一层循环递归之前把数据进行相关处理然后传到下一个循环里,递归结束后要擦除操作,还有一种就是记录每次循环的值,到a=0时统一处理,递归结束后也要擦除记录)
	
	//第一种写法
	static void For_n(int a, int m,StringBuffer sb1,String sl) {  //a代表循环的层数,m代表每层循环的横向范围即0~M
		if(a<=0) {return;}
		else {
			for(int i=0;i<=m;i++) {
				StringBuffer sb = new StringBuffer(sb1);
				sb.insert(i, sl);
				For_n(a-1,m+1,sb,sl);
				StringBuffer stb=new StringBuffer(sb);              //因为sb是可变字符串,在判断istrue的时候已经对他进行了删改,即使sb符合最后加入的都是" "空串
				if(isTrue(sb)) set.add(stb.toString());
			}
		}
	}
	
	//第二种写法(我偏向于这种处理)
	static void for_n(int a, int m,StringBuffer sb1,String sl,int k) {   //a代表循环的层数,m代表每层循环的横向范围即0~M,因为用list记录插入点所以在删除的时候需要知道插入的位置也就是第i次循环-1的索引,用K从0记录
		if(a<=0) {
			StringBuffer sb=new StringBuffer(sb1);
			for(Integer o:list) {
				sb.insert(o, sl);
			}
			StringBuffer stb=new StringBuffer(sb);
			if(isTrue(stb)) set.add(sb.toString());
			
			return;}
		else {
			for(int i=0;i<=m;i++) {
				list.add(i);
				for_n(a-1,m+1,sb1,sl,k+1);
				list.remove(k);                                  
				
			}
		}
	}
	
	static void for_n_Duplicate(int a, int m,StringBuffer sb1,int k) {   //a代表循环的层数,m代表每层循环的横向范围即0~M,因为用list记录插入点所以在删除的时候需要知道插入的位置也就是第i次循环-1的索引,用K从0记录
		if(a<=0) {
			StringBuffer sb=new StringBuffer(sb1);
			for(int i=0;i<list.size()/2;i++) {
				sb.insert(list.get(i), "(");
			}
           for(int z=list.size()/2;z<list.size();z++) {
        	   sb.insert(list.get(z), ")");
			}
			StringBuffer stb=new StringBuffer(sb);
			if(isTrue(stb)) set.add(sb.toString());
			
			return;}
		else {
			for(int i=0;i<=m;i++) {
				list.add(i);
				for_n_Duplicate(a-1,m+1,sb1,k+1);
				list.remove(k);                                  
				
			}
		}
	}

	
	
	
}

  • 2
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值