蓝桥杯Java大学A组-15届省赛学习笔记

蓝桥杯历届真题蓝桥杯历届真题
蓝桥杯评测平台蓝桥杯模拟考试

结果填空题

试题A:拼正方形

解题思路

题目中数据很大,这并不重要,找到解法后100和100万并无本质区别。

首先,如果只有一种2x2规格的正方形,那么平方数个小正方形正好可以拼出一个大正方形,例如4个拼出4x4,9个拼出6x6。

其次,四个1x1规格的正方形可以拼出一个2x2规格的正方形。

结合以上两点,先计算1x1规格的正方形可以拼出多少个2x2规格的正方形,再计算所有2x2规格的正方形可以拼出边长为几的大正方形。

计算结果

5435122


试题B:召唤数学精灵

解题思路

思路一

遍历,计算每个数是否满足条件,但是结算过程中产生的数据太大,没法处理。

思路二

通过数学推导逐步简化问题。

(A(i) - B(i)) % 100 == 0
A(i) % 100 - B(i) % 100 == 0,即 A(i) % 100 == B(i) % 100

当 i 大于等于10时, B(i) % 100 恒等于0,因为总有因子2,5,10。
则 i 大于等于10时, A(i) % 100 等于0时满足题意,
A(i) = (i * (i + 1)) / 2 ,则应有 ( i * (i + 1) ) % 200 == 0

综上,解题步骤应为:
第一步,结合取余运算结果的周期性,确定 i 从1到200之间满足上式的数有几个;
第二步,确定题目中的范围中有几个整周期;
第三步,确定不足一个周期的部分满足题意的数有几个;
第四步,确定 i 小于10的部分满足题意的数有几个。

运算结果

40480826628086

知识点总结

  1. 四则运算的取模处理:
    ( a + b ) % c == ( a % c + b % c ) % c
    ( a - b ) % c == ( a % c - b % c ) % c
    ( a * b ) % c == ( a % c * b % c ) % c
    除法不能直接套用
  2. 取模运算具有周期性
  3. 任意数对 10的m次方的整数倍 取模的结果只与后k位数有关,k为满足10的k次方大于等于该整百数的最小k值。

程序设计题

试题C:数字诗意

解题思路

首先尝试了通过数学方式分解,结果没什么思路。上网搜了下题解,发现大都是通过找规律解决问题。已知2、4、8都不具有诗意,它们都是2的次幂,验证后发现16、32也不具有诗意,那么就可以将问题转换为判断一个数字是否为2的次幂。

代码

import java.util.*;

public class Main {
    public static void main(String[] args){
        Scanner sc = new Scanner(System.in);
        int n = sc.nextInt();
        int cnt = 0;
        long a = 0L;
        for(int i = 0; i < n; i++){
            a = sc.nextLong();
            if( ( a & (a - 1) ) == 0){
                cnt++;
            }
        }
        System.out.print(cnt);
    }
}

知识点总结

  1. 找规律也是一种解题思路
  2. 判断2次幂的技巧方法——i & ( i - 1) == 0
  3. java.lang类库在编译时就加载,无需import语句,包含Math、Integer、String、StringBuilder、StringBuffer、System等类
  4. java.util类库需要使用import语句,包含Scanner、Random、ArrayList等类,较常用,因此在代码第一行使用import java.util.*导入
  5. int型最大值为2的31次方减一,约等于10的9次方;long型最大值为2的63次方减一,约等于10的18次方
  6. 为long型变量赋值时,在字面量后加L标示,否则字面量超出int范围时会出错

试题D:回文数组

解题思路

思路一

将数组二分,调整左半部分,从左半部分最右侧元素开始,向左遍历,若该元素与其左侧相邻元素同时大于(小于)右半部分对应元素,将两者同时减(加)一,否则单独调整该元素,注意单独调整第一个元素(无左侧相邻元素),记录总的调整次数。

思路二

将数组二分,先将左半部分元素替换为a[i] - a[n - i -1],再从左向右遍历左半部分(从右向左同理),记录将当前元素调整为零的次数(即a[i]的绝对值),若与其右侧相邻元素同号,则对其相邻元素同时进行调整,注意单独调整最后一个元素(无右侧相邻元素),记录总的调整次数。

两种思路其实本质相同,只是思路二将思路一中一次一次调整计数优化为直接调整k次并计数

代码

代码一

有双重循环,时间复杂度为O(n^2),部分用例时间超限。

import java.util.*;

public class Main{
    public static void main(String[] args){
        Scanner sc = new Scanner(System.in);
        int n = sc.nextInt();
        int[] a = new int[n];
        long cnt = 0;
        
        for(int i = 0; i < n; i++){
            a[i] = sc.nextInt();
        }
        for(int i = n / 2 - 1; i > 0; i--){
            while(a[i] != a[n - 1 - i]){
                if(a[i] < a[n - 1 - i]){
                    a[i]++;
                    if(a[i - 1] < a[n - i]){
                        a[i - 1]++;
                    }
                } else {
                    a[i]--;
                    if(a[i - 1] > a[n - i]){
                        a[i - 1]--;
                    }
                }
                cnt++;
            }
        }
        cnt += Math.abs(a[0] - a[n - 1]);
        
        System.out.print(cnt);
    }
}
代码二
import java.util.*;

public class Main{
    public static void main(String[] args){
        Scanner sc = new Scanner(System.in);
        int n = sc.nextInt();
        int[] a = new int[n];
        long cnt = 0L;
        
        for(int i = 0; i < n; i++){
            a[i] = sc.nextInt();
        }
        
        for(int i = 0; i < n / 2; i++){
            a[i] = a[i] - a[n - 1 - i];
        }
        
        for(int i = 0; i < n / 2 - 1; i++){
            cnt += Math.abs(a[i]);
            if(a[i] > 0 == a[i + 1] > 0){
                a[i + 1] = Math.abs(a[i]) > Math.abs(a[i + 1]) ? 0 : a[i + 1] - a[i];
            }
        }
        
        cnt += Math.abs(a[n / 2 - 1]);
        
        System.out.print(cnt);
    }
}

知识点总结

  1. 根据题目数据规模约定,数组大小和每个元素不会超过int范围,但是实测发现最终结果超过了int范围需要使用long
  2. 判断是否同号的技巧——a > 0 == b > 0
  3. 涉及贪心算法思想,深入学习参考贪心算法笔记

试题E:吊坠

解题思路

将环形字符串视为点,公共字符串长度为边权,题目就是让求最大生成树
第一步,求出所有边权,可以使用动态规划。
第二步,求出最大生成树的边权之和,可以使用Kruskal算法 。

代码

以下代码源自网络——原链接

import java.util.*;
import java.io.*;
public class Main {
    private static char[][] arr;
    public static void main(String[] args) throws IOException {
        BufferedReader in = new BufferedReader(new InputStreamReader(System.in));
        PrintWriter out = new PrintWriter(System.out);
        StringTokenizer st = new StringTokenizer(in.readLine());
        int n = Integer.parseInt(st.nextToken());
        int m = Integer.parseInt(st.nextToken());
        // 每个字符串重复自己
        arr = new char[n][m * 2];
        for (int i = 0; i < n; i++) {
            char[] s = in.readLine().toCharArray();
            for (int j = 0; j < m; j++) {
                arr[i][j] = arr[i][j + m] = s[j];
            }
        }
        // 求所有边权
        int[][] dist = new int[n][n];
        for (int i = 0; i < n - 1; i++) {
            for (int j = i + 1; j < n; j++) {
                dist[i][j] = lcs(i, j);
            }
        }
        // 所有边入堆
        Queue<int[]> pq = new PriorityQueue<>(Comparator.comparingInt((int[] a) -> -a[2]));
        for (int i = 0; i < n - 1; i++) {
            for (int j = i + 1; j < n; j++) {
                pq.offer(new int[]{i, j, dist[i][j]});
            }
        }
        // Kruskal 算法
        int[] root = new int[n];
        Arrays.setAll(root, a -> a);
        int take = 0;
        int ans = 0;
        while (take < n - 1) {
            int[] p = pq.poll();
            if (find(root, p[0]) != find(root, p[1])) {
                union(root, p[0], p[1]);
                take++;
                ans += p[2];
            }
        }
        out.println(ans);
        out.flush();
        out.close();
    }
    // 求两旋转字符串的最长公共子串长度
    private static int lcs(int x, int y) {
        int m = arr[0].length;
        int max = 0;
        int[][] dp = new int[m + 1][m + 1];
        for (int i = 1; i <= m; i++) {
            for (int j = 1; j <= m; j++) {
                if (arr[x][i - 1] == arr[y][j - 1]) {
                    dp[i][j] = Math.max(dp[i - 1][j - 1] + 1, dp[i][j]);
                    max = Math.max(dp[i][j], max);
                }
            }
        }
        return Math.min(max, m / 2);
    }
    // 并查集
    private static int find(int[] root, int i) {
        return root[i] == i ? i : (root[i] = find(root, root[i]));
    }
    // 并查集
    private static void union(int[] root, int x, int y) {
        root[find(root, x)] = find(root, y);
    }
}

试题F:砍柴

解题思路

属于博弈问题,初始必败态为0、1,根据SG函数的定义求解即可。
SG函数学习——博弈问题与SG函数

代码

import java.util.*;

public class Main {
	public static void main(String[] args) {
		Scanner sc = new Scanner(System.in);
		int T = sc.nextInt();
		int[] n0 = new int[T];//存储原输入顺序
		int[] n = new int[T];
		for (int i = 0; i < T; i++) {
		    n0[i] = n[i] = sc.nextInt();
		}
		
		Arrays.sort(n);
		int maxn = n[T - 1];
		int[] sg = new int[maxn + 1];
		int[] primes = new int[maxn + 1];
        
        for (int i = 0; i <= maxn; i++) {
            sg[i] = 0;
        }
        for (int i = 0; i <= maxn; i++) {
            primes[i] = i;
        }
        
        //埃氏筛
        for (int i = 2; i <= maxn / 2; i++) {
            if (primes[i] != -1) {
                for (int j = 2 * i; j <= maxn; j += i) {
                    primes[j] = -1;
                }
            }
        }
		
		//简便起见,SG函数值直接使用0\1
		for (int i = 2; i <= maxn; i++) {
		    for (int j = i; j >= 2; j--) {
		        if (primes[j] != -1 && sg[i - j] == 0) {
		            sg[i] = 1;
		            break;
		        }
		    }
		}
		
		for (int i = 0; i < T; i++) {
		    System.out.println(sg[n0[i]]);
		}
	}
}

知识点总结

  1. 属于博弈类问题,需要深入学习
  2. 使用埃氏筛找素数,素数筛学习参考埃氏筛与欧拉筛
  3. Arrays类在java.util.Arrays包中,使用Arrays.sort对数组排序,使用Arrays.binarySearch二分查找元素并返回下标,使用Arrays.copyOf复制数组并返回数组,使用Arrays.fill方法填充数组,使用Arrays.equals方法判断数组是否相等并返回布尔结果。

试题G:回文字符串

解题思路

一般情况下将字符串分为3部分:连续的lbq组合(str1)+其余字符+连续的lbq组合(str2),如lb/nbn/ql。

分类讨论:

  1. str1长度大于str2,不能构成回文。
  2. str1长度小于等于str2,从后往前删去字符,使str2与str1等长,判断新字符串是否回文。

需要注意的是,均为lbq时可构成回文

代码

import java.util.Scanner;

public class Main {
	public static void main(String[] args) {
		Scanner sc = new Scanner(System.in);
		int n = sc.nextInt();
		String str = new String();
		String lbq = "lbq";
		
		sc.nextLine();//nextInt()不读空白符
		for (int i = 0; i < n; i++) {
		    str = sc.nextLine();
		    int len = str.length(), len1 = 0, len2 = 0;
		    int left = 0, right = len - 1;
		    while (right >= 0 && lbq.indexOf(str.charAt(right--)) != -1) {
		        len2++;
		    }
		    //先右后左,再通过left < right 的约束使得均为特殊字符时len1为0
		    while (left < right && lbq.indexOf(str.charAt(left++)) != -1) {
		        len1++;
		    }
		    if (len1 > len2) {
		        System.out.println("No");
		    } else if (isPLR(str.substring(0, len - len2 + len1))) {
                System.out.println("Yes");
		    } else {
		        System.out.println("No");
		    }
		}
	}
	
	public static boolean isPLR(String str) {
	    int left = 0;
	    int right = str.length() - 1;
	    while (left < right) {
	        if (str.charAt(left) != str.charAt(right)) {
	            return false;
	        }
	        left++;
	        right--;
	    }
	    return true;
	}
}

知识点总结

  1. 通过双指针实现字符串回文判断
  2. contains参数为字符串,indexOf参数为字符,用contains判断字符串内是否含有字符时,需要用String.valueOf将字符转换为字符串
  3. nextInt不读空白符,next以空白符为间隔,nextLine以换行符为间隔,在nextInt后直接使用nextLine时,需要注意中间有没有换行符,处理不当可能导致nextLine只读取换行符。

试题F:智力测试

难度太大,网上也没找到答案,不必浪费时间,有能力再补。

你可能感兴趣的:(蓝桥杯Java大学A组真题,蓝桥杯,java,学习)