赛前必看 --- 蓝桥杯

2024年6月2日,顺利拿下国赛二等奖

在这里插入图片描述

注意事项

1、数据类型的选择

  • 数据范围:
    • int : 取值范围-2,147,483,648 ~ 2,147,483,647
    • long : 取值范围 -9,223,372,036,854,775,808 ~ 9,223,372,036,854,775,807
  • 数据类型的选择:
    1. 在解题过程中,在初始化数据时需要注意它的数据范围
    2. 输入时的数据一般可以根据题目给出的运行限制确定
    3. 输出时的结果需要根据题意大概估算出最大所需的数据
    4. 如果上述数据范围不满足要求,则需要考虑使用大数(BigInteger)进行保存

2、数组范围:初始化问题

  • 数组最大可以存储 2,147,483,645 个元素,十位数,与 int 类型的取值范围差不多
  • 在使用多维数组时,需要注意它的存储大小!!
  • 比如:
    • 二维数组(len * len):len 的最大取值大概在 10000 左右
    • 三位数组(len * len * len):len 的最大取值大概在 1000 左右

3、其他

  • 当输入有多组数据,但没有明确给出组数时,可以使用sc.hasNext()解决。
  • 只要思想不滑坡,办法总比困难多!!
    • 要学会寻找解题的关键,合理运用算法与数据结构!!

对拍技巧

  • 对拍的含义是,你可以写个暴力程序,时间复杂度很高,但正确性一定能保证的算法,与你编写复杂度足以通过题目的代码,用相同的数据比对输出结果是否相同
  • 一般情况下,建议写一题拍一题,如果手速不快不建议,因为对拍同样浪费时间,但如果你最后几题写不出来,又想拿好的奖项,那么你一定要确保前面的题目正确性。
  • 按照以往的经验,b组,满分150,填空10,大题140。
    • 20分应该能省三,
    • 30+运气好能省二,
    • 70+基本上能省一,如果难点可能60多甚至50多也可以。
    • 因此可能你离获奖就差那5分,同时我们大题目也可以拿部分分,努力将自己总分提上去

数学技巧

  • 小数转整数(向上取整):Math.ceil(x)
  • 小数转整数(向下取整):Math.floor(x)
  • 小数转整数(四舍五入):Math.round(x)

java 日期函数 LocalDate 的使用

1、LocalDate now()

从默认时区的系统时钟获取当前日期。

        LocalDate a = LocalDate.now();        
        System.out.println(a); // 2024-04-11

2、 LocalDate of(int year, int month, int dayOfMonth)

从年、月和日获取实例 LocalDate
year – 代表年份,从MIN_YEAR年到MAX_YEAR年
month – 代表的月份,从1月1日(1月)到12日(12月)
dayOfMonth – 表示从 1 到 31 的月份中的某天

	LocalDate localDate = LocalDate.of(2024, 04, 11);
    System.out.println(localDate);//2024-04-11

3、LocalDate ofYearDay(int year, int dayOfYear)

从一年和一年中的某天获取实例 LocalDate
year – 代表年份,从MIN_YEAR年到MAX_YEAR年
dayOfYear – 代表一年中的一天,从 1 到 366
注意:如果任何字段的值超出范围,或者如果一年中的某一天对年份无效

	LocalDate localDate = LocalDate.ofYearDay(2024 ,32);
	System.out.println(localDate);// 2024-02-01

4、int getYear()

获取年份字段

	LocalDate a = LocalDate.of(2024,04,11);
	int year = a.getYear();
	System.out.println(year); // 2024

5、int getMonthValue()

获取从 1 到 12 的月份字段

	LocalDate a = LocalDate.of(2024,04,11);
	int month = a.getMonthValue();
	System.out.println(month); // 4

6、int getDayOfMonth()

获取月份中的某天字段

	LocalDate a = LocalDate.of(2024,04,11);
	int day = a.getDayOfMonth();
	System.out.println(day); // 11x

7、int getDayOfYear()

获取一年中的某天字段,
返回:一年中的某一天,从 1 到 365,或闰年为 366,也就是当年当月当日的天数

	LocalDate now = LocalDate.of(2024,4,11);
	int localDate = now.getDayOfYear();
	System.out.println(localDate); // 102

8、 boolean isLeapYear()

检查年份是否为闰年

	LocalDate now = LocalDate.of(1904,6,5);
	Boolean localDate = now.isLeapYear();
	System.out.println(localDate); // true

9、int lengthOfMonth()

返回月份有多少天

	LocalDate now = LocalDate.of(2024,4,11);
	int localDate = now.lengthOfMonth();
	System.out.println(localDate); // 30

10、int lengthOfYear()

返回年份有多少天

	LocalDate now = LocalDate.of(2024,4,11);
	int localDate = now.lengthOfYear();
	System.out.println(localDate); // 366

11、LocalDate plusYears(long yearsToAdd)

返回添加 LocalDate 指定年数
参数:
yearsToAdd – 要添加的年份,可能是负数(如果是正数则增加年份,是负数则减年份)

	LocalDate now = LocalDate.of(2024,4,11);
	LocalDate localDate = now.plusYears(2);
	System.out.println(localDate); // 2026-04-11

12、LocalDate plusMonths(long monthsToAdd)

返回添加 LocalDate 指定月数
参数:
monthsToAdd – 要添加的月份,可能是负数(如果是正数则增加月数,是负数则减月数)

	LocalDate now = LocalDate.of(2024,4,11);
	LocalDate localDate = now.plusMonths(-2);
	System.out.println(localDate); // 2024-12-28

13、LocalDate plusWeeks(long weeksToAdd)

返回添加了 LocalDate 指定周数,可能是负数(如果是正数则增加周数,是负数则减周数)

	LocalDate now = LocalDate.of(2023,2,28);
	LocalDate localDate = now.plusWeeks(1);
	System.out.println(localDate); // 2023-03-07

14、LocalDate plusDays(long daysToAdd)

返回添加 LocalDate 指定天数
参数:
daysToAdd – 添加的天数,可能是负数(如果是正数则增加天数,是负数则减天数)

	LocalDate now = LocalDate.of(2023,2,28);
	LocalDate localDate = now.plusDays(10);
	System.out.println(localDate); // 2023-03-10

15、int compareTo(ChronoLocalDate other)

将此日期与另一个日期进行比较。
返回:
比较器值,如果较小则为负值,如果较大则为正值

	LocalDate now = LocalDate.of(2023,1,28);
	LocalDate now1 = LocalDate.of(2023,2,28);
	int localDate = now.compareTo(now1);
	System.out.println(localDate); // -1

Eclipse 调试技巧

  • F5:Step into/跳入方法/进入该行的函数内部
  • F6:Step over/向下逐行调试/一行一行执行
  • F7:Step return/跳出方法/退出当前的函数
  • F8:直接跳转到下一个断点

常用算法(★★★★★)

一、使用前缀和求区间值

1. 一维前缀和

  • 构造一维前缀和数组

    • 假设有一个二维数组 a
    • 需要构造一个前缀和数组 b,则可以按照如下公式进行计算:
      • b[i] = b[i - 1] + a[i]
      • 注意:在初始化数组时,i, j 需要从 1 开始,否则会出现数组越界情况!!!
  • 利用一维前缀和数组计算某一区间的和

    • 假设已知二维数组 a , 前缀和数组 b,求数组 a 中某一区间的和,该区间为[L, R]
    • 解题公式:
      • sum = b[R] - b[L - 1]

2. 二维前缀和

  • 构造二维前缀和数组

    • 假设有一个二维数组 a
    • 需要构造一个前缀和数组 b,则可以按照如下公式进行计算:
      • b[i][j] = b[i - 1][j] + b[i][j - 1] - b[i - 1][j - 1] + a[i][j]
      • 注意:在初始化数组时,i, j 需要从 1 开始,否则会出现数组越界情况!!!
  • 利用二维前缀和数组计算某一区间的和

    • 假设已知二维数组 a , 前缀和数组 b,求数组 a 中某一区间的和,该区间左上角坐标为 [x1, y1] , 右下角坐标为 [x2, y2]
    • 解题公式:
      • sum = b[x2][y2] - b[x1 - 1][y1] - b[x1][y1 - 1] + b[x1 - 1][y1 - 1]

二、差分算法

  • 已知原数组 a ,数组 b 是 a 的差分数组,则:
    • 构造差分数组 b:
      • 当 i = 1 时,b[1] = a[1]
      • 当 i > 1时, b[i] = a[i] + a[i - 1]
    • 差分。通过差分数组,计算数值 a 某一区间加上常数 k 后的数组
      • b[L] + K
      • b[R + 1] - K (注意在计算过程中确保不超出数组范围)
    • 还原原数组 b:
      • 当 i = 1 时,a[1] = b[1]
      • 当 i > 1时, a[i] = b[i] + a[i - 1]
  • 注意:
    • 前缀和与差分都是离线的
      • 即:原数组 a 与 差分数组 b 在修改数据时,不同步,需要不断更新实现同步。

三、欧几里得 – 求最大公约数与最小公倍数

1. 最大公约数

  • 原理:
    • 如果我们需要找到两个正整数1997和615的最大公约数,我们使用欧几里德算法如下所示:
      • 1997/615=3(余数152)
      • 615/152=4(余数7)
      • 152/7=21(余数5)
      • 7/5=1(余数2)
      • 5/2=2(余数1)
      • 2/1=2(余数0)
      • 用除数和余数重复除法运算,当余数为0时,得到1997年和615年的最大公约数1。
	public static int getGYS(int a, int b) { // 求最大公约数  
	    if(b == 0){  
	        return a;  
	    }  
	    return getGYS(b, a % b);  
	}

2. 最小公倍数

  • 原理:两个数的最小公倍数等于两个数的乘积除以它们的最大公约数
	public static int getGBS(int a, int b, int c) {  // 求最小公倍数  
	    int gbs = a * b / getGYS(a, b);  
	    gbs = gbs * c / getGYS(gbs, c);  
	    return gbs;  
	}

四、求一个数的所有因子

public static void fun(long num, ArrayList<Long> arr){  
	//从1遍历到num的平方跟即可。  
	//这时你可能会说,那后面的数呢,比如num本身,不是计算不到了嘛?  
	//别急,看for循环里面的处理情况  
	for ( long i = 1 ; i <= Math.sqrt(num) ; i++ ){  
		//如果能被num整除,那肯定是num的因子,毫无疑问  
		if ( num % i == 0 ){  
			arr.add(i);  
			//重点的部分在这里!!!  
			//当i能被num整除的情况下,此时i是相对较小的因子,用i求出num另一个较大的因子n 
			//因为当i能被num整除时,那么数"num/i"也一定能被num整除  
			//不需要再进行重复的计算,这样算法的运行时间大大降低  
			long n = num / i;  
			//但用i算出另一个较大的因子时,会出现重复的情况  
			//例如num = 4,当遍历到2时,算出另一个较大的因子也是2,这就重复了,要判断一下
			if ( n != i ){  
				arr.add(n);  
			}  
		}  
	}  
}

五、广度优先搜索算法(BFS)

1. 原理

  • 算法描述(遍历过程):

    1. 访问根节点,设置标记,入队列
    2. 队列顶点出队列,同时将该顶点下满足条件的所有孩子顶点进行如下操作:
      1. 判断孩子顶点或构成的路径是否满足场景所需要的条件,比如:在迷宫场景中,寻找到达终点的路线,此场景孩子结点需要满足的条件就是:孩子结点与迷宫终点是同一结点。
        • 如果满足,则返回结果,并退出搜索
        • 否则,继续搜索
      2. 将孩子结点设置对应标记(防止重复遍历,成为死循环)
      3. 将孩子结点入队列
    3. 重复执行第二步操作,直到队列为空,才停止搜索
  • 创建广度优先算法一般所需的数据结构和方法:

    • 队列:用于保存入队列的结点(结点为等待操作的结点)
    • 标志位:用于记录结点访问情况,已访问为 true,未访问为 false
    • void getChildNode(Queue queue, Node ParentNode)
      • 根据提供的父结点 ParentNode,将所有满足条件的孩子结点 ChildNode,入队列 queue,同时将结点的访问情况(标志位)设置为 ture
      • 其中,满足的条件根据场景的不同而不同

2. 算法模板

	void bfs(Node node) {
		Queue queue = new LinkedList<>();
		bj = new boolean[n];
		queue.add(node);
		bj[node.index] = true;
		while(!queue.isEmpty()) {
			Node nowNode = queue.poll();
			LinkedList<Node> nextNodes = getNodes(node);
			while(!nextNodes.isEmpty()) {
				Node nextNode = nextNodes.poll();
				if(满足条件) {
					相关操作
				}
				bj[nextNode.index] = true;
				queue.add(nextNode);
			}
		}
	}

3. 注意事项

  • 如果场景需要寻找路径,则在生成子结点时,需要保存指向其结点的父结点;如果不需要,则不用这一步操作。
  • 在必要的情况下,一定要设置标记位,这样可以节省搜索时间,也可以避免陷入死循环。

六、深度优先搜索算法(DFS)

1. 原理

  • 特点:
    • 每个结点只能访问一次
    • 一步一步向前搜索,无法向前时便回退。
  • 本质:
    • 深搜优先搜索的本质上就是持续搜索,遍历了所有可能的情况,必然能得到解。
  • 搜索过程
    1. 从图中某顶点v出发,访问顶点v;
    2. 依次从v的未被访问的邻接点出发,对图进行深度优先遍历;直至图中和v有路径相通的顶点都被访问;
    3. 若此时图中尚有顶点未被访问,则从一个未被访问的顶点出发,重新进行深度优先遍历,直到图中所有顶点均被访问过为止。

2. 算法模板

	void dfs(Node node) {
		if(满足题目条件) {
			相关操作
		}
		// 通过当前结点获取下一结点
		LinkedList<Node> nextNodes = getNodes(node);
		while(!nextNodes.isEmpty()) {
			Node node = nextNodes.poll();
			// 设置标记
			bj[index] = true;
			dfs(node);
			//释放标记
			bj[index] = false;
		}
	}

	LinkedList<Node> getNodes(Node node) {
		// 获取邻近结点
		// 并进行剪枝操作
	}

七、Java 排序 API 的使用

  • 对数组(整数,字符,字符串,浮点数,二进制数)进行排序(默认升序):
	Arrays.sort(ints);
  • 对数组的指定范围进行排序:
	Arrays.sort(ints, 0, 4);
	// 注意:
	// 指定的排序范围是左闭右开区间:[0, 4)
  • 对数组进行排序(指定是升序还是降序):
	// 需要通过 Comparator 接口实现
	Comparator comparator = (Comparator<Integer>) (o1, o2) -> o2 - o1; // 降序
	Comparator comparator = (Comparator<Integer>) (o1, o2) -> o1 - o2; // 升序

	// 记忆口诀:
	// 降序:o2 - o1
	// 升序:o1 - o2

	// 调用排序API
	Arrays.sort(ints, comparator);
	Arrays.sort(ints, 0, t, comparator);

	// 简便写法:
	Arrays.sort(ints, (o1, o2) -> o2 - o1); //降序
	Arrays.sort(ints, 0, t, (o1, o2) -> o1 - o2); // 升序
  • 对哈希表进行排序(键值对)
	// 已知一个哈希表,如下所示:
	HashMap<Integer, Integer> hashMap = new HashMap<>();
	
	// 对哈希表进行排序
	// 1. 将哈希表转换为ArrayList集合
	ArrayList<Map.Entry<Integer, Integer>> arrayList = new ArrayList<>(hashMap.entrySet());

	// 2. 对ArrayList进行排序
	arrayList.sort(Map.Entry.comparingByKey()); // 通过比较键进行排序
	arrayList.sort(Map.Entry.comparingByValue()); // 通过比较值,进行排序

八、 双指针

  • 定义:
    • 双指针指的是在遍历对象的过程中,不是普通的使用单个指针进行访问,而是使用两个相同方向(快慢指针)或者相反方向(对撞指针)的指针进行扫描,从而达到相应的目的。
    • 严格的来说,双指针只能说是是算法中的一种技巧
  • 最常见的双指针算法有两种:
    • 在一个序列里边,用两个指针维护一段区间。
    • 在两个序列里边,用两个指针分别指向不同序列,来维护某种次序。
  • 双指针算法的核心思想(作用):
    • 优化:在利用双指针算法解题时,考虑原问题如何用暴力算法解出,观察是否可构成单调性,若可以,就可采用双指针算法对暴力算法进行优化.
      • 当我们采用朴素的方法即暴力枚举每一种可能的情况,时间复杂度为O(n*n)
      • 当我们使用双指针算法时通过某种性质就可以将上述O(n*n)的操作优化到O(n)
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值