蓝桥杯算法集训 - 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];
}

Ⅱ、回文日期

2867. 回文日期 - AcWing题库

回文日期

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]);
}

Ⅱ、挤牛奶

1343. 挤牛奶 - AcWing题库

挤牛奶

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

Ⅰ、奶牛选美

2060. 奶牛选美 - AcWing题库

奶牛选美

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;
        }
    }
}

Ⅱ、扫雷

4407. 扫雷 - AcWing题库

扫雷

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);
        }
    }
}

Ⅱ、组合求和

40. 组合总和 II - 力扣(LeetCode)

组合总和

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); // 回退
        }
    }
}
posted @ 2024-03-17 22:19  TfiyuenLau  阅读(45)  评论(0编辑  收藏  举报