前言 :
4月12日蓝桥杯省赛,准备按照自己过往的经验去冲刺,下面这些知识点都是比较基础。但巩固下来省一真的轻轻松松,我去年省一就做了三道半基础题!!所以大家放心冲刺。跟紧我的脚步!!2024届蓝桥杯javaB组省一分享经验&题目_蓝桥杯java可以用idea吗-优快云博客
计划:
准备复习的知识点:快速输入输出,基础api(进制转换),前缀和与差分,二分,双指针,期望,搜索,动态规划,贪心,字符串,队列和栈,树与图(基础部分),模板(快速幂,并查集,gcd,素数筛,逆元)!
天数 | 知识点 | 题目 | 说明 |
---|---|---|---|
第一天 4月7日 | 快速输入输出,前缀和与差分,二分,双指针,基础api(进制转换等等) | 1.ROG 2.棋盘 3,分巧克力 4,全部都有的子序列 | (简单)第一天主要是先初步熟悉api。知道api有哪些,怎么去用。然后刷一些基础算法的题目 时长:4小时左右 |
第二天 4月8日 | 贪心,期望,搜索,基本数据结构(队列,栈,队) | 1,最优分解 2,dfs实现组合型枚举 3,分布式队列 | (中等)(中等)这里面就搜索稍微比较难想到一写 |
第三天 | 动态规划,树与图(基础部分) | (困难) | |
第四天 | 背模板(快速幂,并查集,gcd,素数筛,逆元) | (中等) | |
第五天 | 计时刷真题卷,模拟真实考试场景。 |
第一天
(非常重要)快速输入输出
直接背模版:蓝桥刷题笔记
后面的解题都使用了快输快入,但为了节省篇幅,所以该文章题解只提供了main方法的代码。
(前缀和)题目:ROG(1.5小时)
知识点链接:蓝桥刷题笔记 (可能有的图片不显示,可以查看网页左边,的pdf版本)
题目链接:题库 - 蓝桥云课
思路:先求R串的数量,再求RO串的数量,最后求ROG串的数量!
我的代码:
public static void main(String[] args) { long mod = 998244353; String str = in.nextLine(); String[] params = str.split(" "); int n = Integer.parseInt(params[0]); int m = Integer.parseInt(params[1]); char[][] arr = new char[n][m]; for (int i = 0; i < n; i++) { arr[i] = in.next().toCharArray(); } int[][] arrR = new int[n][m]; int[][] arrO = new int[n][m]; if(arr[0][0] == 'R') { arrR[0][0] = 1; } //记录R的前缀和 for (int i = 1; i < m; i++) { arrR[0][i] = arr[0][i] == 'R' ? arrR[0][i-1]+1 : arrR[0][i-1]; } for (int i = 1; i < n; i++) { arrR[i][0] = arr[i][0] == 'R' ? arrR[i-1][0]+1 : arrR[i-1][0]; } for (int i = 1; i < n; i++) { for (int j = 1; j < m; j++) { int value = arrR[i-1][j] + arrR[i][j-1] - arrR[i-1][j-1]; arrR[i][j] = arr[i][j] == 'R' ? value+1 : value; } } //记录RO的前缀和 for (int i = 1; i < m; i++) { arrO[0][i] = arr[0][i] == 'O' ? arrR[0][i]+arrO[0][i-1] : arrO[0][i-1]; } for (int i = 1; i < n; i++) { arrO[i][0] = arr[i][0] == 'O' ? arrR[i][0]+arrO[i-1][0] : arrO[i-1][0]; } for (int i = 1; i < n; i++) { for (int j = 1; j < m; j++) { int value = arrO[i-1][j] + arrO[i][j-1] - arrO[i-1][j-1]; arrO[i][j] = arr[i][j] == 'O' ? value+arrR[i][j] : value; } } //统计ROG字符串的数量 long count = 0; for (int i = 0; i < n; i++) { for (int j = 0; j < m; j++) { if(arr[i][j] == 'G') { count = (count+arrO[i][j]) % mod; } } } out.println(count); out.flush(); }
通过率:95%
观赏大佬的代码:
import java.util.Scanner; // 1:无需package // 2: 类名必须Main, 不可修改 public class Main { public static void main(String[] args) { Scanner sc = new Scanner(System.in); int n = sc.nextInt(), m = sc.nextInt(); char[][] a = new char[n+2][m+2]; long[][] s_R = new long[n+2][m+2]; long[][] s_G = new long[n+2][m+2]; for (int i = 1; i <= n ; i++) { String row = sc.next(); for (int j = 1; j <= m ; j++) { a[i][j] = row.charAt(j-1); s_R[i][j] = s_R[i][j-1] + s_R[i-1][j] - s_R[i-1][j-1];//前缀和,最后一项需要单独判断,复用数组 s_G[i][j] = s_G[i][j-1] + s_G[i-1][j] - s_G[i-1][j-1]; if(a[i][j] == 'R') s_R[i][j]++; if(a[i][j] == 'G') s_G[i][j]++; } } long mod = 998244353; long ans = 0; long res = 0; for (int i = 1; i <= n ; i++) { for (int j = 1; j <= m ; j++) { if(a[i][j] == 'O'){ ans = (s_R[i][j] * (s_G[n][m] - s_G[i-1][m] - s_G[n][j-1] + s_G[i-1][j-1])) % mod; res = (res + ans)%mod; } } } System.out.println(res); } }
总结:
-
接收数字用spilt方法。
-
对于前缀和,可以多开2个格子,这样就不用额外判断边缘了!!
-
加深前缀和的印象。前缀和可以秒变后缀合。前缀和就是可以o1求出某个块的和!
我的代码差一个样例,不管了,继续下一题!
二维差分(20分钟)
跟前缀和差不多思想,就不写代码了,找个题,写写思路,记住公式就行。
题目:棋盘
链接:7.棋盘 - 蓝桥云课
题目要求就是,n次操作,对某个块进行翻面,最后输出棋盘的样貌。
思路:在二维差分数组上进行操作,最后在前缀和得结果
欣赏大佬的代码:
import java.util.Scanner; // 1:无需package // 2: 类名必须Main, 不可修改 public class Main { public static void main(String[] args) { Scanner sc = new Scanner(System.in); int n = sc.nextInt(); int m = sc.nextInt(); int arr[][] = new int[n + 2][n + 2]; int result[][] = new int[n + 1][n + 1]; while (m-- != 0) {//记录差分数组头尾 int x1 = sc.nextInt(); int y1 = sc.nextInt(); int x2 = sc.nextInt(); int y2 = sc.nextInt(); arr[x1][y1]++; arr[x1][y2 + 1]++; arr[x2 + 1][y1]++; arr[x2 + 1][y2 + 1]++; } for (int i = 1; i < n + 1; i++) { for (int j = 1; j < n + 1; j++) { //求二维差分数组的前缀和 arr[i][j] += arr[i - 1][j] + arr[i][j - 1] - arr[i - 1][j - 1]; if (arr[i][j] % 2 == 0) {//判断是变1还是变0 result[i][j] = 0; } else { result[i][j] = 1; } System.out.print(result[i][j]); } System.out.println(); } sc.close(); } }
总结:
1.大佬的万物都++很巧妙。这样就不用管正负了。直接判断奇偶就行!!
二分(50分钟)
一般用在,数值有序(或者可以把数变有序),然后求最值的题目上。
知识点链接:蓝桥刷题笔记 (可能有的图片不显示,可以查看网页左边,的pdf版本)
题目:分巧克力
链接:题库 - 蓝桥云课
思路:对每块巧克力最小边长进行排序,然后进行二分,求最大的符合要求的边长
代码:
public static void main(String[] args) { int n = in.nextInt(); int k = in.nextInt(); int[][] arr = new int[n][2]; for (int i = 0; i < n; i++) { int a = in.nextInt(); int b = in.nextInt(); if (a<b){ arr[i][0] = a; arr[i][1] = b; }else { arr[i][0] = b; arr[i][1] = a; } } Arrays.sort(arr,(a,b) -> a[0]==b[0]? a[1]-b[1]:a[0]-b[0]); int l = 1; int r = arr[n-1][0]; while (l<r){ int mid = (l+r+1)/2; if (check(arr,k,mid)>0){ l = mid; }else { r = mid-1; } } out.println(l); out.flush(); out.close(); } static int check(int[][] arr, int k, int bian){ int n = arr.length; int count = 0; for (int i = 0; i < n; i++){ count += (arr[i][0]/bian)*(arr[i][1]/bian); } if (count>=k){ return bian; }else { return -1; } }
双指针(40分钟)
题目:全部都有的子序列
题目链接:1.全部都有的子序列 - 蓝桥云课
思路:一看就题目说求蓝桥序列长度最短为多少。求最值,最该想到的就是贪心和二分,这一看肯定贪不了,所以大概率能二分,对长度m(吗个不同元素)-n(数组长度)进行二分。二分判断条件使用双指针!
大佬的代码:
import java.util.HashSet; import java.util.Scanner; import java.util.Set; public class 全部都有的子序列_二分_滑动窗口 { public static void main(String[] args) { Scanner sc=new Scanner(System.in); int n= sc.nextInt(); int []arr=new int[n]; Set<Integer> set=new HashSet<>(); for (int i = 0; i < n; i++) { arr[i]= sc.nextInt(); set.add(arr[i]); } int l=0,r=n; int m=set.size();//set存储不重复的数字 while(l<r){ int mid=(l+r)/2; if(check(mid,arr,m)) r=mid; else l=mid+1; } System.out.println(l); } public static boolean check(int mid,int []arr,int m){//滑动窗口求解 int n=arr.length; int []f=new int[1001];//记录出现频率 int l=0,r=0;//双指针 int ans=0;//记录长度 while(r<n) {//右指针没有到达数组最右边 f[arr[r]]++;//记录一个数的频率 if(f[arr[r]]==1){ ans++;} if(r-l+1>mid) {//当区间距离>mid,说明此时并没有满足ans>=m f[arr[l]]--;//左指针对应减一 if(f[arr[l]]==0){//说明之前只有一个,减去后变成零,这个时候一个数字消失,对应的ans应减一 ans--; } l++;//左指针右移 } r++;//右指针一直右移 if(ans>=m) return true; } return false; } }
基础api(30分钟)
知识点链接:蓝桥刷题笔记 (可能有的图片不显示,可以查看网页左边,的pdf版本)
题目:无。该知识点仅需记忆即可!
第二天
贪心
算法思路:感觉就是暴力,找最优解
题目:无
概率与期望(40分钟)
题目:最优分解
题目链接:题库 - 蓝桥云课
思路:去年的省赛题!!!记住期望公式就好做了!!
代码:
import java.util.Scanner; // 1:无需package // 2: 类名必须Main, 不可修改 public class Main { public static void main(String[] args) { Scanner sc=new Scanner(System.in); // N组 int N=sc.nextInt(); // 感染的概率 double p=sc.nextDouble(); // 没感染的概率 double q=(1-p); // 如果1个组内的宠物大于1只宠物,初始化S的值为N+1; double s=N+1; // 因为最少有一只宠物,所以初始化K的值为1; int k=1; // 遍历从1到N个宠物 for(int i=1;i<=N;i++) { // 如果正好只有N一个宠物那么S,K也等于1 if(i==1) { s=N;k=1; // 如果不是那么当i是N的因子的时候, }else if(N%i==0) { // w等于q的i次方;概率为:分别有q为每个不被感染的概率有i个, // 所以是i的次方,拿1减去不被感染的概率就是感染的概率; double w=1-Math.pow(q, i); // N/i的原因是有N个有i组, // 这个分组如果比之前的更好就重新循环; if(w*N+(N/i)<s) { s=w*N+(N/i); k=i; } } } System.out.print(k); } }
搜索(40分钟)
知识点链接:蓝桥刷题笔记 (可能有的图片不显示,可以查看网页左边,的pdf版本)
题目:dfs实现组合型枚举
题目链接:题库 - 蓝桥云课
代码:
import java.util.*; public class Main { static Scanner scan = new Scanner(System.in); public static void main(String[] args) { int n = scan.nextInt(); // 总数 int m = scan.nextInt(); // 选出的数字 boolean f[] = new boolean[n+10]; Arrays.fill(f, false); dfs(1,n,m,f); } static void dfs(int i, int n, int m, boolean[] f) { // TODO Auto-generated method stub // 如果还需要选的数量为 0 if (m == 0) { for (int j = 1; j <= n; j++) { if (f[j]) { System.out.print(j + " "); } } System.out.println(); return; } // 如果选用的数量为0 但是已经越界 if (i == n+1) { return; } // 选当前位 m-1 f[i] = true; dfs(i+1, n, m-1, f); f[i] = false; dfs(i+1, n, m, f); } }
基本数据结构(队列,栈,堆)
知识点链接:蓝桥刷题笔记 (可能有的图片不显示,可以查看网页左边,的pdf版本)
学习队列,栈,堆的调用!!
题目:分布式队列(去年省赛题!)
题目链接:题库 - 蓝桥云课
代码:
import java.util.*; public class Main { public static void main(String[] args) { Scanner sc = new Scanner(System.in); //n为有几个元素 int n = sc.nextInt(); //创建长度为n的数组,数组的含义是第i个队列中有几个元素,0下标表示主队列有几个元素 int arr[] = new int[n]; //开始模拟 while(sc.hasNext()) { //接收需要的操作 String op = sc.next(); switch (op) { //添加操作 case "add": arr[0]++; break; //同步操作 case "sync": int index = sc.nextInt(); //如果副队列与主队列数量相等,说明已同步完,副队列没有可同步数据 if(arr[0]==arr[index]) break; //否则就进行同步 else { arr[index]++; break; } //查询操作 case "query": //查询的意思就是,找副队列中最小数量 int min = Integer.MAX_VALUE; for (int i = 0; i < arr.length; i++) { //从0开始,是因为有个细节,当n为一时,只有主队列 min = Math.min(min, arr[i]); } System.out.println(min); } } } }