蓝桥杯算法集训 - Week 3:日期问题、区间合并、DFS、回溯
蓝桥杯算法集训 - Week 3
本系列随笔用于整理AcWing题单——《蓝桥杯集训·每日一题2024》的系列题型及其对应的算法模板。
一、日期问题
Ⅰ、代码模板
static int months[13] = {
0, 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31
};
static int isLeap(int year) {
if (year % 4 == 0 && year % 100 != 0 || year % 400 == 0)
return 1;
return 0;
}
static int getDays(int year, int month) {
if (month == 2)
return months[month] + isLeap(year);
return months[month];
}
Ⅱ、回文日期
import java.util.Scanner;
public class Main {
static final int[] monthToDay = { 0, 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 };
static int s;
public static void main(String[] args) {
Scanner sc = new Scanner(System.in);
s = sc.nextInt();
sc.close();
int res1 = 0, res2 = 0;
boolean flag = false;
for (int i = 1000; i < 10000; i++) { // 枚举合法年份
int n = i, x = i;
for (int j = 0; j < 4; j++) { // 拼接年份的回文日期
n = n * 10 + x % 10;
x /= 10;
}
if (n > s && check(n)) {
if (!flag) {
res1 = n;
flag = true;
}
int a = n / 10000000; // 日期第一位
int b = n / 1000000 % 10; // 日期第二位
int ab1 = n / 1000000; // 日期第一二位
int ab2 = n / 10000 % 100; // 日期第二三位
if (ab1 == ab2 && a != b) { // 判断回文是否为abab型且不为aaaa型
res2 = n;
break;
}
}
}
System.out.println(res1);
System.out.println(res2);
}
// 校验日期合法性
private static boolean check(int n) {
int y = n / 10000;
int m = n % 10000 / 100;
int d = n % 10000 % 100;
monthToDay[2] = y % 4 == 0 && y % 100 != 0 || y % 400 == 0 ? 29 : 28;
if (m < 1 || m > 12)
return false;
if (d < 1 || d > monthToDay[m])
return false;
return true;
}
}
二、区间合并
Ⅰ、代码模板
static final int N = 110;
static int m;
static int[][] matrix = new int[N][2]; // 记录N个区间的矩阵
// 区间合并
static int[][] merge() {
if (m == 1) {
return matrix;
}
// 对区间按左边界从小到大排序
Arrays.sort(matrix, 0, m, (a, b) -> Integer.compare(a[0], b[0]));
List<int[]> list = new ArrayList<>();
int temp[] = matrix[0];
list.add(temp);
// 遍历每个区间,判断是否与上一个区间重叠
for (int i = 1; i < m; i++) {
if (temp[1] < matrix[i][0]) {
list.add(matrix[i]);
temp = matrix[i];
} else if (temp[1] <= matrix[i][1]) {
list.remove(temp);
temp = new int[] {temp[0], matrix[i][1]};
list.add(temp);
}
}
return list.toArray(new int[list.size()][2]);
}
Ⅱ、挤牛奶
import java.io.*;
import java.util.*;
public class Main {
static final int N = 5010;
static int n, res1 = 0, res2 = 0;
static int[][] matrix = new int[N][2];
public static void main(String[] args) throws NumberFormatException, IOException {
BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
n = Integer.parseInt(br.readLine());
for (int i = 0; i < n; i++) {
String[] split = br.readLine().split(" ");
matrix[i][0] = Integer.parseInt(split[0]);
matrix[i][1] = Integer.parseInt(split[1]);
}
br.close();
merge(matrix);
System.out.println(res1 + " " + res2);
}
// 区间合并
static int[][] merge(int[][] intervals) {
if (n == 1) {
res1 = intervals[0][1] - intervals[0][0];
return intervals;
}
// 区间由小到大排序
Arrays.sort(intervals, 0, n, (a, b) -> Integer.compare(a[0], b[0]));
List<int[]> list = new ArrayList<>();
int[] temp = intervals[0];
list.add(temp);
res1 = intervals[0][1] - intervals[0][0];
for (int i = 1; i < n; i++) {
// 当前区间与temp有无相交
if (temp[1] < intervals[i][0]) {
res2 = Math.max(res2, intervals[i][0] - temp[1]); // 记录最长空闲时间
list.add(intervals[i]);
temp = intervals[i];
} else if (temp[1] <= intervals[i][1]) {
res1 = Math.max(res1, intervals[i][1] - temp[0]); // 记录最长连续挤奶时间
list.remove(temp);
temp = new int[] {temp[0], intervals[i][1]};
list.add(temp);
}
}
res1 = Math.max(res1, intervals[n - 1][1] - intervals[n - 1][0]); // 处理最后一组时间
return list.toArray(new int[list.size()][2]);
}
}
三、DFS
Ⅰ、奶牛选美
import java.io.*;
import java.util.*;
public class Main {
static final int N = 100;
static int n, m;
static char[][] matrix;
static List<List<Pair>> points = new ArrayList<>();
static int[] dx = { 0, 1, 0, -1 }, dy = { -1, 0, 1, 0 }; // 矩阵dfs方向
public static void main(String[] args) throws IOException {
BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
String[] split = br.readLine().split(" ");
n = Integer.parseInt(split[0]);
m = Integer.parseInt(split[1]);
matrix = new char[n][m];
for (int i = 0; i < n; i++) {
matrix[i] = br.readLine().toCharArray();
}
points.add(new ArrayList<>());
points.add(new ArrayList<>());
br.close();
// DFS搜索矩阵的两个斑点点集
int k = 0;
for (int i = 0; i < n; i++) {
for (int j = 0; j < m; j++) {
if (matrix[i][j] == 'X')
dfs(i, j, points.get(k++));
}
}
// 遍历两个斑点点集,并求最小距离
int res = 110;
for (Pair a : points.get(0)) {
for (Pair b : points.get(1)) {
res = Math.min(res, Math.abs(a.x - b.x) + Math.abs(a.y - b.y));
}
}
System.out.println(res - 1);
}
// 递归搜索一个点的相邻点
static void dfs(int a, int b, List<Pair> p) {
p.add(new Pair(a,b));
matrix[a][b] = '.';
// 遍历矩阵的四个方向
for (int i = 0; i < 4; i++) {
int x = a + dx[i], y = b + dy[i];
if (x >= 0 && y >= 0 && x < n && y < m && matrix[x][y] == 'X') {
dfs(x, y, p);
}
}
}
static class Pair {
int x, y;
public Pair(int x, int y) {
this.x = x;
this.y = y;
}
}
}
Ⅱ、扫雷
import java.io.*;
import java.util.*;
public class Main {
static final int N = 50010;
static int n, m, res = 0;
static Map<Long, int[]> map = new HashMap<>();
public static void main(String[] args) throws IOException {
BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
String[] split = br.readLine().split(" ");
n = Integer.parseInt(split[0]);
m = Integer.parseInt(split[1]);
for (int i = 0; i < n; i++) {
split = br.readLine().split(" ");
int x = Integer.parseInt(split[0]), y = Integer.parseInt(split[1]) + 10, r = Integer.parseInt(split[2]);
long pos = (long) 2e9 * x + y; // 映射long值作为pos的key
map.putIfAbsent(pos, new int[2]);
int[] a = map.get(pos);
a[0]++;
a[1] = Math.max(a[1], r);
}
for (int i = 0; i < m; i++) {
split = br.readLine().split(" ");
int x = Integer.parseInt(split[0]), y = Integer.parseInt(split[1]) + 10, r = Integer.parseInt(split[2]);
long pos = (long) 2e9 * x + y;
dfs(pos, r);
}
br.close();
System.out.println(res);
}
// 递归搜索半径为r的地雷
static void dfs(long pos, int r) {
// 还原 x, y 坐标
int x = (int) (pos / (int) 2e9), y = (int) (pos % (int) 2e9);
if (map.containsKey(pos)) { // pos上存在地雷
int a[] = map.remove(pos);
r = Math.max(r, a[1]);
res += a[0];
}
// 遍历半径的 x, y 轴
for (int i = -r; i <= r; i++) {
for (int j = -r; j <= r; j++) {
if (i * i + j * j > r * r || i == 0 && j == 0) { // 超出半径
continue;
}
long p = (long) 2e9 * (x + i) + y + j;
dfs(p, 0); // 递归深度为0表示不传播到其他点
}
}
}
}
四、回溯
回溯算法复习参考:第 13 章 回溯 - Hello 算法
Ⅰ、代码模板
/* 回溯算法框架 */
static void backtrack(State state, List<Choice> choices, List<State> res) {
// 判断是否为解
if (isSolution(state)) {
// 记录解
recordSolution(state, res);
// 不再继续搜索
return;
}
// 遍历所有选择
for (Choice choice : choices) {
// 剪枝:判断选择是否合法
if (isValid(state, choice)) {
// 尝试:做出选择,更新状态
makeChoice(state, choice);
// 递归:进行下一轮选择
backtrack(state, choices, res);
// 回退:撤销选择,恢复到之前的状态
undoChoice(state, choice);
}
}
}
Ⅱ、组合求和
class Solution {
public List<List<Integer>> combinationSum2(int[] candidates, int target) {
List<List<Integer>> res = new ArrayList<List<Integer>>();
Arrays.sort(candidates); // 排序优化
backtrace(new ArrayList<Integer>(), target, 0, candidates, res);
res = new ArrayList<List<Integer>>(new HashSet<>(res)); // 暴力去重
return res;
}
// 回溯 DFS 查找每种组合
public void backtrace(List<Integer> state, int target, int start, int[] choices, List<List<Integer>> res) {
if (target == 0) { // 符合题意的数字组合
res.add(new ArrayList<Integer>(state));
return;
}
for (int i = start; i < choices.length; i++) {
if (i > start && choices[i] == choices[i - 1]) { // 保证选择不重复
continue;
}
int choice = choices[i];
if (choice > target) {
break;
}
state.add(choice); // 尝试
backtrace(state, target - choice, i + 1, choices, res); // 递归
state.remove(state.size() - 1); // 回退
}
}
}
本文来自博客园,作者:TfiyuenLau,转载请注明原文链接:https://www.cnblogs.com/tfiyuenlau/p/18079316