java 洛谷题单【算法2-1】前缀和、差分与离散化
P8218 【深进1.例1】求区间和
解题思路
前缀和数组:
prefixSum[i]
表示数组 a 的前 (i) 项的和。- 通过
prefixSum[r] - prefixSum[l - 1]
可以快速计算区间 ([l, r]) 的和。时间复杂度:
- 构建前缀和数组的时间复杂度是 (O(n))。
- 每次查询的时间复杂度是 (O(1))。
- 总体时间复杂度从原来的 (O(m \cdot n)) 降低到 (O(n + m))。
空间复杂度:
- 额外使用了一个长度为 (n + 1) 的前缀和数组,空间复杂度是 (O(n))。
这里首先使用常规思路解题,思路没错,但是会超时,使用前缀后之后,用空间换时间避免超时。
常规思路
import java.util.Scanner;public class Main {public static void main(String[] args) {Scanner input = new Scanner(System.in);int n = input.nextInt();int[] a = new int[n];for (int i = 0; i < a.length; i++) {a[i] = input.nextInt();}int m = input.nextInt();int ans = 0;while (m-- > 0) {int l = input.nextInt();int r = input.nextInt();for (int i = l; i <= r; i++) {ans += a[i - 1];}System.out.println(ans);ans = 0;}input.close();}
}
前缀和(AC代码)
import java.util.Scanner;public class Main {public static void main(String[] args) {Scanner input = new Scanner(System.in);int n = input.nextInt();int[] a = new int[n];int[] prefixSum = new int[n + 1]; // 前缀和数组,prefixSum[0] = 0// 读取数组并计算前缀和for (int i = 0; i < n; i++) {a[i] = input.nextInt();prefixSum[i + 1] = prefixSum[i] + a[i];}int m = input.nextInt();while (m-- > 0) {int l = input.nextInt();int r = input.nextInt();// 使用前缀和计算区间和int ans = prefixSum[r] - prefixSum[l - 1];System.out.println(ans);}input.close();}
}
P1719 最大加权矩形
解题思路
枚举上下边界:
- 使用两个嵌套循环
top
和bottom
,分别表示矩形的上边界和下边界。- 在固定上下边界后,将矩阵压缩为一维数组
temp
,其中temp[col]
表示当前上下边界内第col
列的累加和。Kadane 算法:
- 对压缩后的一维数组
temp
使用 Kadane 算法,快速计算最大子数组和。- Kadane 算法的时间复杂度是 (O(n))。
更新最大值:
- 每次计算出当前上下边界内的最大子数组和后,更新全局最大值
maxSum
。
import java.util.Scanner;public class Main {public static void main(String[] args) {Scanner input = new Scanner(System.in);int n = input.nextInt();int[][] a = new int[n][n];for (int i = 0; i < a.length; i++) {for (int j = 0; j < a.length; j++) {a[i][j] = input.nextInt();}}int maxSum = Integer.MIN_VALUE;// 枚举上下边界for (int top = 0; top < n; top++) {int[] temp = new int[n]; // 用于存储列的累加和for (int bottom = top; bottom < n; bottom++) {// 计算当前上下边界内每列的累加和for (int col = 0; col < n; col++) {temp[col] += a[bottom][col];}// 使用 Kadane 算法计算一维数组的最大子数组和maxSum = Math.max(maxSum, maxSubArraySum(temp));}}System.out.println(maxSum);input.close();}// 一维最大子数组和(Kadane 算法)private static int maxSubArraySum(int[] arr) {int maxSum = arr[0];int currentSum = arr[0];for (int i = 1; i < arr.length; i++) {currentSum = Math.max(arr[i], currentSum + arr[i]);maxSum = Math.max(maxSum, currentSum);}return maxSum;}
}
P1314 [NOIP 2011 提高组] 聪明的质监员
解题思路
输入处理:
- 使用
BufferedReader
和StringTokenizer
读取输入。- 矿石的重量和价值存储在数组 w 和 v 中。
- 区间的左右端点存储在数组 l 和 r 中。
二分查找:
- 使用变量
ans
表示当前的候选参数。
- 从高位到低位逐步尝试增加
的值,直到找到满足条件的最大 $W$。
辅助函数
Y
:
- 计算当前参数
对应的检验值
。
- 使用前缀和数组
s1
和s2
快速计算区间内的权重和价值总和。结果计算:
- 比较
和
,取与
差值的最小值作为最终结果。
import java.io.*;
import java.util.*;public class Main {public static void main(String[] args) throws IOException {BufferedReader br = new BufferedReader(new InputStreamReader(System.in));StringTokenizer st = new StringTokenizer(br.readLine());// 读取矿石数量 n、区间数量 m 和目标值 sint n = Integer.parseInt(st.nextToken());int m = Integer.parseInt(st.nextToken());long s = Long.parseLong(st.nextToken());// 读取每个矿石的重量 w 和价值 vint[] w = new int[n + 1];int[] v = new int[n + 1];for (int i = 1; i <= n; i++) {st = new StringTokenizer(br.readLine());w[i] = Integer.parseInt(st.nextToken());v[i] = Integer.parseInt(st.nextToken());}// 读取每个区间的左右端点int[][] intervals = new int[m][2];for (int i = 0; i < m; i++) {st = new StringTokenizer(br.readLine());intervals[i][0] = Integer.parseInt(st.nextToken());intervals[i][1] = Integer.parseInt(st.nextToken());}// 找到矿石的最大重量,用于二分查找的右边界int max_w = 0;for (int i = 1; i <= n; i++) {if (w[i] > max_w) {max_w = w[i];}}// 初始化二分查找的左右边界int left = 0;int right = max_w + 1;long minDiff = Long.MAX_VALUE; // 记录最小的 |s - y|// 辅助数组,用于前缀和计算long[] cnt = new long[n + 1]; // 记录满足条件的矿石数量的前缀和long[] sum_v = new long[n + 1]; // 记录满足条件的矿石价值的前缀和// 二分查找,寻找使 |s - y| 最小的参数 Wwhile (left <= right) {int mid = (left + right) >>> 1; // 取中间值作为当前的 W// 重置前缀和数组Arrays.fill(cnt, 0);Arrays.fill(sum_v, 0);// 计算前缀和数组for (int i = 1; i <= n; i++) {if (w[i] >= mid) { // 如果当前矿石的重量满足条件cnt[i] = cnt[i - 1] + 1; // 累加满足条件的矿石数量sum_v[i] = sum_v[i - 1] + v[i]; // 累加满足条件的矿石价值} else {cnt[i] = cnt[i - 1]; // 不满足条件,数量保持不变sum_v[i] = sum_v[i - 1]; // 不满足条件,价值保持不变}}// 计算所有区间的检验值 ylong total = 0;for (int i = 0; i < m; i++) {int l = intervals[i][0];int r = intervals[i][1];// 计算区间 [l, r] 内满足条件的矿石数量和价值long count = cnt[r] - cnt[l - 1];long sum = sum_v[r] - sum_v[l - 1];total += count * sum; // 累加到总检验值}// 计算当前检验值与目标值 s 的差值long diff = Math.abs(total - s);if (diff < minDiff) { // 更新最小差值minDiff = diff;}// 根据当前检验值调整二分查找的范围if (total > s) {left = mid + 1; // 检验值过大,增大 W} else {right = mid - 1; // 检验值过小,减小 W}}// 输出最小的 |s - y|System.out.println(minDiff);}
}
P2367 语文成绩
解题思路
- 差分数组:差分数组允许我们在O(1)时间内完成区间加减操作。差分数组
d
的第i
个元素表示原数组a
中第i
个元素与前一个元素的差值。- 区间操作处理:对于每个区间操作
(x, y, z)
,我们只需在差分数组的d[x]
加上z
,并在d[y+1]
减去z
。- 前缀和计算:通过计算差分数组的前缀和,我们可以得到每个学生的最终成绩,并找到其中的最小值。
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.StreamTokenizer;public class Main {public static void main(String[] args) throws IOException {BufferedReader br = new BufferedReader(new InputStreamReader(System.in));StreamTokenizer st = new StreamTokenizer(br);st.nextToken();int n = (int) st.nval;st.nextToken();int p = (int) st.nval;int[] d = new int[n + 2];// 构造差分数组st.nextToken();int aPrev = (int) st.nval;d[1] = aPrev;for (int i = 2; i <= n; i++) {st.nextToken();int aCurrent = (int) st.nval;d[i] = aCurrent - aPrev;aPrev = aCurrent;}// 处理区间操作for (int i = 0; i < p; i++) {st.nextToken();int x = (int) st.nval;st.nextToken();int y = (int) st.nval;st.nextToken();int z = (int) st.nval;d[x] += z;d[y + 1] -= z;}// 计算前缀和并找最小值int sum = 0;int min = Integer.MAX_VALUE;for (int i = 1; i <= n; i++) {sum += d[i];if (sum < min) {min = sum;}}System.out.println(min);}
}
P3397 地毯
解题思路
- 二维差分数组:差分数组是一种用于高效处理区间更新操作的数据结构。通过记录区间的起点和终点的变化量,我们可以在最后通过前缀和每个点的最终覆盖次数。
- 差分操作:对于每个地毯覆盖的矩形区域,我们更新差分数组的四个角点,分别进行加1和减1操作。
- 前缀和计算:通过两次前缀和计算(先按行,再按列),我们可以将差分数组转换为每个点的实际覆盖次数。
import java.util.Scanner;public class Main {public static void main(String[] args) {Scanner input = new Scanner(System.in);int n = input.nextInt();int m = input.nextInt();int[][] d = new int[n + 2][n + 2]; // 使用n+2的数组来避免越界for (int k = 0; k < m; k++) {int x1 = input.nextInt();int y1 = input.nextInt();int x2 = input.nextInt();int y2 = input.nextInt();// 应用差分数组的四个操作d[x1][y1]++;d[x1][y2 + 1]--;d[x2 + 1][y1]--;d[x2 + 1][y2 + 1]++;}// 计算行前缀和for (int i = 1; i <= n + 1; i++) {for (int j = 1; j <= n + 1; j++) {d[i][j] += d[i][j - 1];}}// 计算列前缀和for (int j = 1; j <= n + 1; j++) {for (int i = 1; i <= n + 1; i++) {d[i][j] += d[i - 1][j];}}// 输出结果for (int i = 1; i <= n; i++) {StringBuilder sb = new StringBuilder();for (int j = 1; j <= n; j++) {sb.append(d[i][j]);if (j < n) {sb.append(" ");}}System.out.println(sb);}input.close();}
}
P1496 火烧赤壁
解题思路
输入处理:将所有的区间存储在
List<int[]>
中,每个区间用一个长度为 2 的数组表示。排序:按区间的起点升序排序,如果起点相同,则按终点升序排序。
区间合并:遍历排序后的区间列表,判断当前区间是否与前一个区间重叠:
- 如果不重叠,则将前一个区间的长度累加到总长度中,并更新当前区间为新的起点和终点。
- 如果重叠,则更新当前区间的结束点。
输出结果:最后将最后一个区间的长度累加到总长度中。
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.Scanner;public class Main {public static void main(String[] args) {Scanner input = new Scanner(System.in);int n = input.nextInt();// 用于存储所有区间List<int[]> intervals = new ArrayList<>();for (int i = 0; i < n; i++) {int a = input.nextInt();int b = input.nextInt();intervals.add(new int[]{a, b});}// 按起点排序,如果起点相同按终点排序Collections.sort(intervals, (o1, o2) -> o1[0] == o2[0] ? o1[1] - o2[1] : o1[0] - o2[0]);// 合并区间并计算总长度int totalLength = 0;int start = intervals.get(0)[0];int end = intervals.get(0)[1];for (int i = 1; i < intervals.size(); i++) {int[] current = intervals.get(i);if (current[0] <= end) {// 当前区间与前一个区间重叠或相连,更新结束点end = Math.max(end, current[1]);} else {// 当前区间与前一个区间不重叠,累加长度并更新起点和终点totalLength += end - start;start = current[0];end = current[1];}}// 累加最后一个区间的长度totalLength += end - start;System.out.println(totalLength);input.close();}
}
P1955 [NOI2015] 程序自动分析
解题思路
- 初始化并查集:高效处理连通性问题,每个元素初始时是独立的集合。
- 处理相等约束:将相等的元素合并到同一个集合。
- 处理不等约束:
- 检查不等的元素是否已经连通。
- 如果连通,则违反约束,返回
NO
。- 输出结果:如果所有约束都满足,返回
YES
。
import java.util.*;public class Main {// Union-Find 数据结构,用于高效处理连通性问题static class UnionFind {private final Map<Integer, Integer> parent = new HashMap<>();// 查找操作,带路径压缩优化public int find(int x) {if (!parent.containsKey(x)) { // 如果 x 不在 parent 中,初始化为自身parent.put(x, x);}if (parent.get(x) != x) { // 路径压缩,将 x 的父节点直接指向根节点parent.put(x, find(parent.get(x)));}return parent.get(x);}// 合并操作,将两个集合合并public void union(int x, int y) {int rootX = find(x); // 找到 x 的根节点int rootY = find(y); // 找到 y 的根节点if (rootX != rootY) { // 如果根节点不同,合并两个集合parent.put(rootY, rootX);}}// 判断两个元素是否在同一个集合中public boolean connected(int x, int y) {return find(x) == find(y);}}// 处理单个测试用例,判断约束是否满足public static String solveCase(List<int[]> constraints) {UnionFind uf = new UnionFind(); List<int[]> notEqualPairs = new ArrayList<>(); // 存储不等约束的对// 遍历所有约束for (int[] constraint : constraints) {int x = constraint[0]; int y = constraint[1]; int e = constraint[2]; // 约束类型 (1 表示相等,0 表示不等)if (e == 1) {uf.union(x, y); // 如果是相等约束,合并两个元素} else {notEqualPairs.add(new int[] { x, y }); // 如果是不等约束,加入列表}}// 检查所有不等约束是否被违反for (int[] pair : notEqualPairs) {if (uf.connected(pair[0], pair[1])) { // 如果两个元素已经连通,则违反约束return "NO"; }}return "YES"; }public static void main(String[] args) {Scanner input = new Scanner(System.in); int t = input.nextInt(); StringBuilder result = new StringBuilder(); // 用于存储所有测试用例的结果// 遍历每个测试用例for (int i = 0; i < t; i++) {int n = input.nextInt(); // 读取当前测试用例的约束数量List<int[]> constraints = new ArrayList<>(); // 存储当前测试用例的约束for (int j = 0; j < n; j++) {int x = input.nextInt(); int y = input.nextInt(); int e = input.nextInt(); // 读取约束类型 (1 表示相等,0 表示不等)constraints.add(new int[] { x, y, e }); // 将约束加入列表}String res = solveCase(constraints); // 处理当前测试用例result.append(res).append("\n"); }System.out.print(result); input.close();}
}
P1884 [USACO12FEB] Overplanting S
解题思路
离散化坐标:
- 由于坐标范围很大(
-10^8
到10^8
),直接操作会导致内存和时间复杂度过高。- 我们可以将所有矩形的横坐标和纵坐标进行离散化,映射到一个较小的索引范围。
扫描线算法:
- 按照矩形的横坐标(
x
值)排序,依次处理每个矩形的左边界和右边界。- 使用一个差分数组或线段树来维护当前被覆盖的纵坐标区间。
计算面积:
- 每次扫描到一个新的横坐标时,根据当前的覆盖区间计算面积增量。
- 面积增量等于当前覆盖的纵坐标长度乘以横坐标的变化量。
import java.util.*;public class Main {static class Event implements Comparable<Event> {int x, y1, y2, type; // x坐标,y区间,type=1表示矩形左边界,type=-1表示右边界public Event(int x, int y1, int y2, int type) {this.x = x;this.y1 = y1;this.y2 = y2;this.type = type;}@Overridepublic int compareTo(Event other) {return this.x - other.x; // 按x坐标排序}}public static void main(String[] args) {Scanner input = new Scanner(System.in);int n = input.nextInt();List<Event> events = new ArrayList<>();Set<Integer> yCoords = new HashSet<>();// 读取矩形信息for (int i = 0; i < n; i++) {int x1 = input.nextInt();int y1 = input.nextInt();int x2 = input.nextInt();int y2 = input.nextInt();// 确保y1 < y2if (y1 > y2) {int temp = y1;y1 = y2;y2 = temp;}// 添加事件events.add(new Event(x1, y1, y2, 1)); // 左边界events.add(new Event(x2, y1, y2, -1)); // 右边界// 收集所有y坐标yCoords.add(y1);yCoords.add(y2);}// 离散化y坐标List<Integer> sortedY = new ArrayList<>(yCoords);Collections.sort(sortedY);Map<Integer, Integer> yIndex = new HashMap<>();for (int i = 0; i < sortedY.size(); i++) {yIndex.put(sortedY.get(i), i);}// 按x坐标排序事件Collections.sort(events);// 差分数组,记录每个离散化y区间的覆盖次数int[] count = new int[sortedY.size()];long totalArea = 0;int prevX = events.get(0).x;// 扫描线处理for (Event event : events) {int currX = event.x;// 计算当前覆盖的y区间长度long coveredLength = 0;for (int i = 0; i < count.length - 1; i++) {if (count[i] > 0) {coveredLength += sortedY.get(i + 1) - sortedY.get(i);}}// 累加面积totalArea += coveredLength * (currX - prevX);// 更新差分数组int y1Index = yIndex.get(event.y1);int y2Index = yIndex.get(event.y2);for (int i = y1Index; i < y2Index; i++) {count[i] += event.type;}// 更新prevXprevX = currX;}// 输出结果System.out.println(totalArea);input.close();}
}
P2004 领地选择
解题思路
第七个测试点如果MLE,多试几次就能过。
二维前缀和:
- 构建一个二维前缀和数组
prefix
,其中prefix[i][j]
表示从地图左上角(1, 1)
到(i, j)
的矩形区域的土地价值总和。- 通过前缀和,可以在常数时间内计算任意矩形区域的总和。
滑动窗口计算最大值:
- 遍历所有可能的首都左上角坐标
(x, y)
,计算以(x, y)
为左上角、边长为C
的正方形区域的总价值。- 记录最大值及其对应的坐标。
优化:
- 使用二维前缀和可以将矩形区域的求和从
O(C^2)
优化到O(1)
,整体复杂度为O(N * M)
。
import java.util.*;public class Main {public static void main(String[] args) {Scanner input = new Scanner(System.in);// 读取输入int N = input.nextInt();int M = input.nextInt();int C = input.nextInt();int[][] grid = new int[N + 1][M + 1]; // 地图,1-based 索引for (int i = 1; i <= N; i++) {for (int j = 1; j <= M; j++) {grid[i][j] = input.nextInt();}}// 构建二维前缀和int[][] prefix = new int[N + 1][M + 1];for (int i = 1; i <= N; i++) {for (int j = 1; j <= M; j++) {prefix[i][j] = grid[i][j]+ prefix[i - 1][j]+ prefix[i][j - 1]- prefix[i - 1][j - 1];}}// 滑动窗口寻找最大价值int maxSum = Integer.MIN_VALUE;int bestX = 0, bestY = 0;for (int i = C; i <= N; i++) {for (int j = C; j <= M; j++) {// 计算以 (i, j) 为右下角,边长为 C 的正方形的总价值int total = prefix[i][j]- prefix[i - C][j]- prefix[i][j - C]+ prefix[i - C][j - C];if (total > maxSum) {maxSum = total;bestX = i - C + 1;bestY = j - C + 1;}}}// 输出结果System.out.println(bestX + " " + bestY);input.close();}
}
P3017 [USACO11MAR] Brownie Slicing G
解题思路
前缀和计算
构建二维前缀和数组s
,其中s[i][j]
表示从(1,1)
到(i,j)
的子矩阵和。通过前缀和,可以快速计算任意子矩阵的和,例如行区间[now+1, i]
和列j
的和为:
(s[i][j] - s[i][j-1]) - (s[now][j] - s[now][j-1])
。二分答案
在可能的范围[0, 矩阵总和]
内进行二分查找,寻找最大的x
,使得矩阵可以被切割成a
行b
列,每块的和均≥x
。检查函数
check
- 逐行扫描:从第一行开始,累计当前行与上一次切割行之间的列差值。
- 动态切割:当累计值≥
x
时切割一列,并统计当前行切割出的列数。若某行切割出至少b
列,则记为该行有效,并更新切割位置。- 结果判定:若有效行数≥
a
,则当前x
可行。
import java.io.*;
import java.util.*;public class Main {static int r, c, a, b; // 矩阵行数、列数,目标切割成a行b列static int[][] map = new int[501][501]; // 输入的矩阵数据(1-based)static int[][] s = new int[501][501]; // 二维前缀和数组(1-based)static int ans; // 存储最终结果static boolean check(int x) {int now = 0; // 记录上一次切割的行号(初始从第0行开始)int num = 0; // 统计已切割的行数for (int i = 1; i <= r; i++) { // 遍历每一行int dis = 0; // 当前累计的差值int sum = 0; // 当前行切割出的列数for (int j = 1; j <= c; j++) {// 计算第j列在[now+1, i]行之间的和:当前行j列前缀和 - 上一次切割行j列前缀和int current = (s[i][j] - s[i][j - 1]) - (s[now][j] - s[now][j - 1]);if (dis + current < x) { // 累加后仍不足x,继续累积dis += current;} else { // 累积足够,切割一列sum++;dis = 0; // 重置累计值}}if (sum >= b) { // 当前行能切割出至少b列now = i; // 更新切割行num++; // 增加切割行计数}}return num >= a; // 是否满足至少a行}public static void main(String[] args) throws IOException {BufferedReader br = new BufferedReader(new InputStreamReader(System.in));StringTokenizer st = new StringTokenizer(br.readLine());r = Integer.parseInt(st.nextToken());c = Integer.parseInt(st.nextToken());a = Integer.parseInt(st.nextToken());b = Integer.parseInt(st.nextToken());// 读取矩阵数据(1-based)for (int i = 1; i <= r; i++) {st = new StringTokenizer(br.readLine());for (int j = 1; j <= c; j++) {map[i][j] = Integer.parseInt(st.nextToken());}}// 计算二维前缀和数组s[i][j](1-based)for (int i = 1; i <= r; i++) {for (int j = 1; j <= c; j++) {s[i][j] = s[i - 1][j] + s[i][j - 1] + map[i][j] - s[i - 1][j - 1];}}int h = 0; // 二分左边界(最小可能值)int t = s[r][c]; // 二分右边界(矩阵总和,最大可能值)ans = 0;// 二分查找寻找最大可行的xwhile (h <= t) {int mid = (h + t) / 2;if (check(mid)) { // 当前mid可行,尝试更大的值ans = mid;h = mid + 1;} else { // 不可行,减小阈值t = mid - 1;}}System.out.println(ans);}
}
P3406 海底高铁
解题思路
输入处理:读取城市数量和访问顺序,以及每段铁路的票价信息。
差分数组:使用差分数组来高效统计每段铁路的经过次数。差分数组可以在O(1)时间内处理区间增操作,最后通过前缀和得到每段铁路的实际经过次数。
费用计算:根据每段铁路的经过次数,比较购买纸质车票和使用IC卡的总费用,选择较小的那个累加到总费用中。
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;public class Main {public static void main(String[] args) throws IOException {BufferedReader br = new BufferedReader(new InputStreamReader(System.in));String[] line = br.readLine().split(" ");int N = Integer.parseInt(line[0]);int M = Integer.parseInt(line[1]);line = br.readLine().split(" ");int[] P = new int[M];for (int i = 0; i < M; i++) {P[i] = Integer.parseInt(line[i]);}int[] d = new int[N + 2]; // 差分数组,索引0到N+1for (int j = 0; j < M - 1; j++) {int u = P[j];int v = P[j + 1];int l = Math.min(u, v);int r = Math.max(u, v) - 1;d[l]++;if (r + 1 <= N) {d[r + 1]--;}}int[] cnt = new int[N + 1]; // 段i的次数是cnt[i]int sum = 0;for (int i = 1; i <= N - 1; i++) {sum += d[i];cnt[i] = sum;}long total = 0;for (int i = 1; i <= N - 1; i++) {line = br.readLine().split(" ");int A = Integer.parseInt(line[0]);int B = Integer.parseInt(line[1]);int C = Integer.parseInt(line[2]);int k = cnt[i];if (k == 0) {continue;}long cost1 = (long) k * A;long cost2 = (long) k * B + C;total += Math.min(cost1, cost2);}System.out.println(total);}
}
P1083 [NOIP 2012 提高组] 借教室
解题思路
差分数组原理
- 核心思想:将区间操作转换为端点标记
当处理订单[l,r]
增加d时:
diff[l] += d
:表示从l开始所有元素增加ddiff[r+1] -= d
:表示从r+1开始取消这个增加- 前缀和计算:最终通过计算diff数组的前缀和,得到每个位置的实际变化量
二分查找优化
- 搜索目标:第一个导致资源不足的订单(最小的非法订单号)
- 循环不变量:保持
[0,left)
区间合法,[left,right)
区间待检查- 终止条件:当
left == right
时,left即为第一个非法订单索引大数处理方案
- 数据类型选择:
rest/diff/dArr
使用long类型:防止处理1e9级数值时溢出current
使用long类型:防止累加过程溢出int范围- 输入处理优化:使用
(long)st.nval
直接读取浮点数转long,保留完整精度
import java.io.*;
import java.util.*;public class Main {static int n, m; // 天数、订单数static long[] rest; // 每日可用教室数(1-based)static long[] diff; // 差分数组(记录每日变化量)static long[] dArr; // 订单需求量数组(long防溢出)static int[] lArr; // 订单左端点数组static int[] rArr; // 订单右端点数组static boolean isValid(int x) {Arrays.fill(diff, 0); // 重置差分数组// 应用前x个订单到差分数组for (int i = 0; i < x; i++) {int l = lArr[i];diff[l] += dArr[i]; // 区间起点增加需求if (rArr[i] + 1 <= n) { // 防止越界diff[rArr[i] + 1] -= dArr[i]; // 区间终点后一位减少需求}}// 2. 计算每日累计需求并验证long current = 0; // 使用long防止累加溢出for (int i = 1; i <= n; i++) {current += diff[i]; // 计算前缀和得到实际需求if (current > rest[i]) { // 发现资源不足return false;}}return true;}public static void main(String[] args) throws IOException {// 输入加速:使用StreamTokenizer处理大输入StreamTokenizer st = new StreamTokenizer(new BufferedReader(new InputStreamReader(System.in)));st.nextToken(); n = (int) st.nval; // 天数st.nextToken(); m = (int) st.nval; // 订单数// 初始化每日教室数量数组(注意1-based)rest = new long[n + 2]; for (int i = 1; i <= n; i++) {st.nextToken();rest[i] = (long) st.nval;}// 存储订单数据(0-based)dArr = new long[m]; // 需求值存在大数,必须用longlArr = new int[m]; // 区间左端点(1-based)rArr = new int[m]; // 区间右端点(1-based)for (int i = 0; i < m; i++) {st.nextToken(); dArr[i] = (long) st.nval; // 处理大整数st.nextToken(); lArr[i] = (int) st.nval;st.nextToken(); rArr[i] = (int) st.nval;}// 初始化差分数组(大小与rest对齐)diff = new long[n + 2]; // 快速检查:所有订单都合法时直接输出if (isValid(m)) {System.out.println(0);return;}// 二分查找核心逻辑(左闭右开区间)int left = 0, right = m; while (left < right) {int mid = (left + right) >>> 1; // 无符号右移防溢出if (isValid(mid + 1)) { // 检查前mid+1个订单是否合法left = mid + 1; // 合法则尝试更大的值} else {right = mid; // 非法则缩小右边界}}// 输出第一个非法订单编号(从1开始)System.out.println("-1");System.out.println(left + 1); }
}
P2882 [USACO07MAR] Face The Right Way G
解题思路
贪心策略:从左到右遍历每个位置,如果发现当前牛朝后,则立即进行一次翻转操作。这样可以确保后面的处理不会影响到已经处理过的位置。
队列维护:使用队列来记录翻转操作的结束位置,以便在处理后续位置时能够正确跟踪当前翻转的影响。
二分查找优化:通过遍历所有可能的 K 值,找到使得操作次数最少的 K。
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.util.ArrayDeque;
import java.util.Deque;public class Main {public static void main(String[] args) throws IOException {BufferedReader br = new BufferedReader(new InputStreamReader(System.in));int N = Integer.parseInt(br.readLine());int[] state = new int[N];// 读取每一行状态,将 'B' 转换为 1,其他字符转换为 0for (int i = 0; i < N; i++) {String line = br.readLine().trim();state[i] = line.charAt(0) == 'B' ? 1 : 0;}// 初始化答案变量,ansK 表示最优的 K 值,ansM 表示最小的翻转次数int ansK = N;int ansM = Integer.MAX_VALUE;for (int K = 1; K <= N; K++) {Deque<Integer> queue = new ArrayDeque<>(); // 用于记录当前翻转区间的结束位置int current = 0; // 当前翻转状态的累积值int cnt = 0; // 当前翻转次数boolean valid = true; // 标记当前 K 是否有效// 遍历状态数组for (int i = 1; i <= N; i++) {// 移除已经过期的翻转区间while (!queue.isEmpty() && queue.peekFirst() <= i) {queue.pollFirst();current--;}int idx = i - 1; // 当前索引(从 0 开始)int cs = state[idx] ^ (current % 2); // 计算当前状态是否需要翻转if (cs == 1) { // 如果需要翻转// 如果翻转区间超出数组范围,则当前 K 无效if (i + K - 1 > N) {valid = false;break;}cnt++; // 增加翻转次数int end = i + K; // 计算翻转区间的结束位置queue.addLast(end); // 将结束位置加入队列current++; // 更新当前翻转状态}}// 如果当前 K 有效,更新最优解if (valid) {// 如果翻转次数更少,或者翻转次数相同但 K 更小,则更新答案if (cnt < ansM || (cnt == ansM && K < ansK)) {ansM = cnt;ansK = K;}}}System.out.println(ansK + " " + ansM);}
}
P4552 [Poetize6] IncDec Sequence
解题思路
- 输入处理:使用
BufferedReader
读取输入,将每个元素存储在数组a
中。- 差分计算:遍历数组,计算相邻元素的差值
diff
,并分别累加到正数总和pos
或负数绝对值总和neg
。- 最少操作次数:取
pos
和neg
的最大值,因为每次操作可以处理一个正数和一个负数单位,剩余部分需要单独处理。- 可能的结果数:计算
pos
和neg
的差的绝对值加1,因为剩余的操作可以调整最终值的不同可能性。
import java.io.*;public class Main {public static void main(String[] args) throws IOException {BufferedReader br = new BufferedReader(new InputStreamReader(System.in));int n = Integer.parseInt(br.readLine()); long[] a = new long[n]; for (int i = 0; i < n; i++) {a[i] = Long.parseLong(br.readLine());}// 计算差分数组中正差和负差的绝对值和long pos = 0; // 存储所有正差的总和(a[i] - a[i-1] > 0)long neg = 0; // 存储所有负差的绝对值总和(a[i] - a[i-1] < 0)for (int i = 1; i < n; i++) {long diff = a[i] - a[i - 1]; // 计算当前元素与前一个元素的差值if (diff > 0) {pos += diff; // 累加正差} else {neg += -diff; // 累加负差的绝对值(取反后相加)}}// 计算最少操作次数:等于正差和负差中较大的那个(每次操作可以抵消一个正差和一个负差)long operations = Math.max(pos, neg);// 计算可能的结果种数:正差与负差的差值绝对值 + 1(剩余的操作次数可以自由分配到不同位置)long variations = Math.abs(pos - neg) + 1;System.out.println(operations); // 输出最少操作次数System.out.println(variations); // 输出可能的结果种数}
}
P3029 [USACO11NOV] Cow Lineup S
解题思路
数据结构定义:
Cow
类用于存储每头牛的坐标和品种,并实现Comparable
接口以便根据坐标排序。输入处理与排序:
- 使用
BufferedReader
读取输入数据,将每头牛的信息存入数组,并统计所有不同品种。- 根据牛的坐标对数组进行排序,确保后续滑动窗口处理的有序性。
滑动窗口逻辑:
- 初始化:左指针
left
、品种计数器count
、最小窗口长度minLength
和品种计数哈希表breedCount
。- 右指针移动:遍历每头牛(作为窗口右端点),更新品种计数。当某品种首次出现时,增加计数器。
- 窗口收缩:当窗口包含所有品种时,不断右移左指针以缩小窗口,并更新最小窗口长度。左指针移动时,减少对应品种的计数,若某品种计数归零则减少计数器。
import java.io.*;
import java.util.*;public class Main {// 定义Cow类,存储牛的坐标和品种,并实现Comparable接口以便排序static class Cow implements Comparable<Cow> {int x;int breed;public Cow(int x, int breed) {this.x = x;this.breed = breed;}// 按x坐标升序排序@Overridepublic int compareTo(Cow o) {return Integer.compare(this.x, o.x);}}public static void main(String[] args) throws IOException {BufferedReader br = new BufferedReader(new InputStreamReader(System.in));int n = Integer.parseInt(br.readLine());Cow[] cows = new Cow[n];Set<Integer> breeds = new HashSet<>(); // 用于统计所有不同的品种// 读取输入并初始化牛的数组for (int i = 0; i < n; i++) {StringTokenizer st = new StringTokenizer(br.readLine());int x = Integer.parseInt(st.nextToken());int breed = Integer.parseInt(st.nextToken());cows[i] = new Cow(x, breed);breeds.add(breed);}Arrays.sort(cows); // 按x坐标排序int k = breeds.size(); // 不同品种的总数if (k == 1) { // 特殊情况:只有一种品种,无需移动System.out.println(0);return;}int left = 0; // 滑动窗口左指针int count = 0; // 当前窗口中不同品种的数量long minLength = Long.MAX_VALUE; // 最小窗口长度Map<Integer, Integer> breedCount = new HashMap<>(); // 记录窗口中各品种的出现次数for (int right = 0; right < n; right++) {int currentBreed = cows[right].breed;// 更新当前品种的计数breedCount.put(currentBreed, breedCount.getOrDefault(currentBreed, 0) + 1);if (breedCount.get(currentBreed) == 1) { // 首次出现该品种,增加计数器count++;}// 当窗口包含所有品种时,尝试缩小窗口以找到最小长度while (count == k) {// 计算当前窗口的x坐标差,并更新最小值minLength = Math.min(minLength, cows[right].x - cows[left].x);int leftBreed = cows[left].breed;// 左指针右移,更新品种计数breedCount.put(leftBreed, breedCount.get(leftBreed) - 1);if (breedCount.get(leftBreed) == 0) { // 该品种在窗口中不再存在,减少计数器count--;}left++;}}System.out.println(minLength);}
}
P1904 天际线
解题思路
事件处理:
- 每个建筑生成两个事件:左边缘(开始)和右边缘(结束)。
- 事件按x坐标升序排序,同一x下开始事件优先处理,确保正确的覆盖顺序。
高度管理:
- 使用
TreeMap
维护当前活动的高度及其出现次数,便于快速获取最大值(lastKey()
)。轮廓线生成:
- 遍历处理所有事件,每次处理同一x的所有事件后检查当前最大高度。
- 仅当高度变化时记录转折点,避免冗余点。
输出格式:
- 结果列表按顺序拼接为字符串,符合题目要求的交替x和y坐标格式。
import java.io.*;
import java.util.*;public class Main {static class Event {int x;int h;boolean isStart;public Event(int x, int h, boolean isStart) {this.x = x;this.h = h;this.isStart = isStart;}}public static void main(String[] args) throws IOException {BufferedReader br = new BufferedReader(new InputStreamReader(System.in));List<Event> events = new ArrayList<>();String line;// 读取所有建筑数据并生成事件列表while ((line = br.readLine()) != null && !line.isEmpty()) {StringTokenizer st = new StringTokenizer(line);int L = Integer.parseInt(st.nextToken());int H = Integer.parseInt(st.nextToken());int R = Integer.parseInt(st.nextToken());events.add(new Event(L, H, true)); // 开始事件events.add(new Event(R, H, false)); // 结束事件}// 事件排序:按x升序,同一x下开始事件优先Collections.sort(events, (a, b) -> {if (a.x != b.x) return a.x - b.x;// 开始事件排在结束事件前面if (a.isStart && !b.isStart) return -1;if (!a.isStart && b.isStart) return 1;return 0;});TreeMap<Integer, Integer> heightCount = new TreeMap<>();List<Integer> result = new ArrayList<>();int prevHeight = 0;int i = 0;while (i < events.size()) {int currentX = events.get(i).x;// 处理同一x的所有事件int j = i;while (j < events.size() && events.get(j).x == currentX) {Event event = events.get(j);if (event.isStart) {// 添加高度计数heightCount.put(event.h, heightCount.getOrDefault(event.h, 0) + 1);} else {// 减少高度计数,若为0则移除int cnt = heightCount.getOrDefault(event.h, 0);if (cnt == 1) {heightCount.remove(event.h);} else if (cnt > 1) {heightCount.put(event.h, cnt - 1);}}j++;}// 计算当前最大高度int currHeight = heightCount.isEmpty() ? 0 : heightCount.lastKey();if (currHeight != prevHeight) {result.add(currentX);result.add(currHeight);prevHeight = currHeight;}i = j; // 移动到下一个x的事件}// 构建输出字符串StringBuilder sb = new StringBuilder();for (int k = 0; k < result.size(); k++) {if (k > 0) sb.append(" ");sb.append(result.get(k));}System.out.println(sb);}
}
P4375 [USACO18OPEN] Out of Sorts G
解题思路
- 数据结构定义:
Data
类用于保存元素的值(val
)和原始位置(num
),并实现Comparable
接口以支持排序。- 输入处理:读取输入数据并构建
Data
数组,使用1-based索引以匹配原C++代码逻辑。- 排序操作:使用
Arrays.sort
对数组的1到n部分进行排序,排序规则按值和原始位置升序排列。- 标记数组与计数:
vis
数组用于标记元素的原始位置是否被处理。cnt
记录当前需要处理的元素数,ans
记录最大的cnt
值,即最大“moo”次数。- 遍历与更新逻辑:遍历排序后的数组,根据元素原始位置和当前位置的关系更新计数,并维护最大计数值。
import java.util.*;// 定义数据结构,保存元素的值和原始位置
class Data implements Comparable<Data> {int val;int num;public Data(int val, int num) {this.val = val;this.num = num;}// 实现比较方法:先按值升序,值相同则按原始位置升序@Overridepublic int compareTo(Data other) {if (this.val != other.val) {return Integer.compare(this.val, other.val);} else {return Integer.compare(this.num, other.num);}}
}public class Main {public static void main(String[] args) {Scanner input = new Scanner(System.in);int n = input.nextInt();// 使用1-based索引,数组大小为n+1以便直接使用1到n的索引Data[] a = new Data[n + 1];for (int i = 1; i <= n; i++) {int val = input.nextInt();a[i] = new Data(val, i); // 记录元素的原始位置(从1开始)}// 对数组的1到n部分进行排序Arrays.sort(a, 1, n + 1);// 初始化访问标记数组,记录每个位置是否被处理过boolean[] vis = new boolean[n + 2]; // 防止越界int cnt = 0;int ans = 1; // 至少会有一个moo输出for (int i = 1; i <= n; i++) {// 如果当前元素的原始位置在当前位置之后,需要处理if (i < a[i].num) {cnt++;}// 如果当前位置已被访问过,减少计数(可能已处理完毕)if (vis[i]) {cnt--;}// 标记当前元素的原始位置已被处理vis[a[i].num] = true;// 更新最大计数,即最大的moo次数ans = Math.max(ans, cnt);}System.out.println(ans);input.close();}
}
P5937 [CEOI 1999] Parity Game
解题思路
- Query类:存储每个查询的x、y坐标和奇偶性z。
- 并查集实现:
find
函数:路径压缩优化,确保快速查找根节点。union
函数:合并两个集合。- 离散化处理:
- 使用TreeSet收集所有出现的坐标点并排序。
- 将坐标映射到连续的索引,缩小数据范围。
- 处理查询:
- 将每个查询的坐标转换为离散化后的索引。
- 根据奇偶性类型(z),合并对应的节点并检查矛盾。若发现矛盾立即输出当前查询索引。
- 输出结果:若所有查询均无矛盾,输出查询总数m。
import java.util.*;
import java.io.*;public class Main {static class Query {int x, y, z;Query(int x, int y, int z) {this.x = x;this.y = y;this.z = z;}}static int[] parent;static int find(int x) {if (parent[x] != x) {parent[x] = find(parent[x]); // 路径压缩}return parent[x];}static void union(int x, int y) {int fx = find(x);int fy = find(y);if (fx != fy) {parent[fx] = fy;}}public static void main(String[] args) throws IOException {BufferedReader br = new BufferedReader(new InputStreamReader(System.in));int n = Integer.parseInt(br.readLine());int m = Integer.parseInt(br.readLine());Query[] queries = new Query[m];Set<Integer> points = new TreeSet<>();// 读取所有查询并收集坐标点for (int i = 0; i < m; i++) {String[] parts = br.readLine().split(" ");int x = Integer.parseInt(parts[0]) - 1; // 转换为前缀差形式int y = Integer.parseInt(parts[1]);int z = parts[2].equals("odd") ? 1 : 0;queries[i] = new Query(x, y, z);points.add(x);points.add(y);}// 离散化处理List<Integer> sorted = new ArrayList<>(points);int size = sorted.size();parent = new int[size * 2 + 2]; // 每个点对应两个节点// 初始化并查集for (int i = 0; i < parent.length; i++) {parent[i] = i;}// 处理每个查询for (int i = 0; i < m; i++) {Query q = queries[i];int x = Collections.binarySearch(sorted, q.x);int y = Collections.binarySearch(sorted, q.y);x = x < 0 ? -x - 1 : x; // 处理binarySearch的返回值y = y < 0 ? -y - 1 : y;int xSame = x;int xDiff = x + size;int ySame = y;int yDiff = y + size;if (q.z == 0) { // even: x和y奇偶性相同if (find(xSame) == find(yDiff)) {System.out.println(i);return;}union(xSame, ySame);union(xDiff, yDiff);} else { // odd: x和y奇偶性不同if (find(xSame) == find(ySame)) {System.out.println(i);return;}union(xSame, yDiff);union(xDiff, ySame);}}System.out.println(m);}
}
相关文章:
java 洛谷题单【算法2-1】前缀和、差分与离散化
P8218 【深进1.例1】求区间和 解题思路 前缀和数组: prefixSum[i] 表示数组 a 的前 (i) 项的和。通过 prefixSum[r] - prefixSum[l - 1] 可以快速计算区间 ([l, r]) 的和。 时间复杂度: 构建前缀和数组的时间复杂度是 (O(n))。每次查询的时间复杂度是 …...
FoundationPose 4090部署 真实场景迁移
参考链接: github代码 4090部署镜像拉取 前期准备 搜狗输入法安装 4090双屏不ok:最后发现是hdmi线坏了。。。。 demo 复现 环境部署(docker本地化部署) 拉取镜像 docker pull shingarey/foundationpose_custom_cuda121:late…...
[dp14_回文串] 分割回文串 II | 最长回文子序列 | 让字符串成为回文串的最少插入次数
目录 1.分割回文串 II 题解 2.最长回文子序列 题解 3.让字符串成为回文串的最少插入次数 题解 回文串,想通过s[i] s[j] 来实现状态变化,由二维数组 右下角 开始扩散 1.分割回文串 II 链接: 132. 分割回文串 II 给你一个字符串 s&…...
美团一面总结
八股的问题里Spring存在不足,无法将八股的知识和项目串联起来。 记录几个值得研究的问题: 端口80到8080是怎么到的 又是怎么一步一步到controller? [用户请求80端口] ↓ [Nginx监听80端口并转发 → 8080] ↓ [Tomcat监听8080端口,…...
Selenium2+Python自动化:利用JS解决click失效问题
文章目录 前言一、遇到的问题二、点击父元素问题分析解决办法实现思路 三、使用JS直接点击四、参考代码 前言 在使用Selenium2和Python进行自动化测试时,我们有时会遇到这样的情况:元素明明已经被成功定位,代码运行也没有报错,但…...
PyTorch的benchmark模块
PyTorch的benchmark模块主要用于性能测试和优化,包含核心工具库和预置测试项目两大部分。以下是其核心功能与使用方法的详细介绍: 1. 核心工具:torch.utils.benchmark 这是PyTorch内置的性能测量工具,主要用于代码片段的执行时间…...
基于MLKit的Android人脸识别应用开发实践
基于MLKit的Android人脸识别应用开发实践 https://gitee.com/wenhua512/face-recognition 1. 项目概述 1.1 功能特点 实时人脸检测与跟踪人脸特征提取与识别自动/手动采集模式人脸数据管理相机参数优化 1.2 技术选型 MLKit人脸检测MediaPipe人脸网格CameraX相机框架Room数…...
【技术派后端篇】ElasticSearch 实战指南:环境搭建、API 操作与集成实践
1 ES介绍及基本概念 ElasticSearch是一个基于Lucene 的分布式、高扩展、高实时的基于RESTful 风格API的搜索与数据分析引擎。 RESTful 风格API的特点: 接受HTTP协议的请求,返回HTTP响应;请求的参数是JSON,返回响应的内容也是JSON…...
Spring Boot 应用程序中配置使用consul
配置是 Spring Boot 应用程序中的一部分,主要用于配置服务端口、应用名称、Consul 服务发现以及健康检查等功能。以下是对每个部分的详细解释: 1. server.port server:port: 8080作用:指定 Spring Boot 应用程序运行的端口号。解释…...
【设计模式——策略模式】
为什么要使用策略模式? 策略模式是一种行为设计模式,它允许在运行时选择算法或行为。通过将算法封装在独立的类中,客户端可以在运行时动态地选择和切换算法,而无需修改原有代码。这种模式特别适合需要灵活切换行为的场景。 形象…...
helm账号密码加密
1、安装工具 sudo apt update sudo apt install gnupg -y wget https://github.com/getsops/sops/releases/download/v3.10.2/sops-v3.10.2.linux.amd64 mv sops-v3.10.2.linux.amd64 /usr/local/bin/sops chmod x /usr/local/bin/sops2、生成加密文件 gpg --full-generate-…...
【今日三题】添加字符(暴力枚举) / 数组变换(位运算) / 装箱问题(01背包)
⭐️个人主页:小羊 ⭐️所属专栏:每日两三题 很荣幸您能阅读我的文章,诚请评论指点,欢迎欢迎 ~ 目录 添加字符(暴力枚举)数组变换(位运算)装箱问题(01背包) 添加字符(暴力枚举) 添加字符 当在A的开头或结尾添加字符直到和B长度…...
数据处理与GUI开发场景下Python常见类型错误解析与应对策略
数据处理与GUI开发场景下Python常见类型错误解析与应对策略 前言 Python 作为一种广泛应用于数据处理和 GUI 开发的高级编程语言,其动态类型特性为开发者带来了极大的灵活性,但同时也容易引发各种类型错误。在数据处理中,从数据采集、清洗到…...
【论文阅读笔记】模型的相似性
文章目录 The Platonic Representation Hypothesis概述表征收敛的依据表征收敛的原因实验依据未来发展的局限性 Similarity of Neural Network Representations Revisited概述问题背景相似性度量s的性质可逆线性变换不变性正交变换不变性各向同性缩放不变性典型度量满足的性质 …...
MVC协同工作流程
1. 视图层(View)代码作用 核心代码示例(以JSP为例): <!-- register.jsp --> <form action"registerServlet" method"post">用户名: <input type"text" na…...
OpenGL shader开发实战学习笔记:第十章 法线贴图
1. 10 法线贴图 1.1. 什么是法线贴图 我们如果想要在盾牌上实现凹凸感,应该如何做?一种方法是添加更多的顶点来建模更多的细节,但是网格的顶点越多,渲染网格所需的顶点着色器计算就越多,网格占用的内存就越多。大多数…...
神经光子渲染:物理级真实感图像生成——从麦克斯韦方程到深度学习
一、技术背景与核心突破 2025年,神经光子渲染(Photonic Neural Rendering, PNR)技术通过物理光学方程与神经辐射场的深度融合,在AIGC检测工具(如GPTDetector 5.0)的识别准确率从98%降至12%。该技术突破性地…...
MCP 协议知识分享
MCP 协议知识分享 一、MCP 协议概述1.1 定义与背景1.2 核心价值1.3 与传统 API 的对比 二、技术架构与工作原理2.1 核心组件2.2 通信机制2.3 典型工作流程 三、关键技术与应用场景3.1 核心技术3.2 典型应用场景 四、与微软技术的集成4.1 Azure OpenAI 服务4.2 Playwright MCP 服…...
spring boot 文件下载
1.添加文件下载工具依赖 Commons IO is a library of utilities to assist with developing IO functionality. <dependency><groupId>commons-io</groupId><artifactId>commons-io</artifactId><version>2.6</version> </depe…...
Redis --- 基本数据类型
Redis --- 基本数据类型 Redis Intro5种基础数据类型 Redis Intro Redis(Remote Dictionary Server)是一款开源的高性能键值存储系统,常用于缓存、消息中间件和实时数据处理场景。以下是其核心特点、数据类型及典型使用场景: 核心…...
随机IP的重要性:解锁网络世界的无限可能
IP地址不仅是连接互联网的“身份证”,更是企业、开发者和个人用户实现高效运营与安全防护的核心工具。然而,固定IP的局限性日益凸显——从隐私泄露到访问受限,从爬虫封禁到商业竞争壁垒,这些问题如何破解?答案就是随机…...
C#: 用Libreoffice实现Word文件转PDF
现实场景中要实现Word格式转PDF格式还是比较常见的。 如果要用开源的组件,只有用Libreoffice了。 一、下载安装Libreoffice 先进入如下链接,找到最新版本和匹配的操作系统来安装。 官网试过,下载是能下载,但安装了用不了&…...
客户验收标准模糊,如何明确
客户验收标准模糊往往会导致项目延迟、质量不符合期望或客户不满意,明确验收标准的关键在于与客户的充分沟通、制定清晰的文档、并确保双方对目标一致性达成共识。在项目的执行过程中,如果客户未能明确表达他们的验收标准,或者项目团队未能确…...
Halcon应用:九点标定-手眼标定
提示:若没有查找的算子,可以评论区留言,会尽快更新 Halcon应用:九点标定-手眼标定 前言一、Halcon应用?二、应用实战1、图形理解[eye-to-hand]:1.1、开始应用2 图形理解[eye-in-hand] 前言 本篇博文主要用…...
springboot3 cloud gateway 配置websocket代理转发教程
前言 最近微服务的项目,需要集成websocket的功能,我在其中的一个微服务模块中集成websocket代码实现,通过模块的端口测试正常,但是通过springboot cloud gateway的端口访问,连接失败!我通过各种百度、和AI…...
详解与FTP服务器相关操作
目录 什么是FTP服务器 搭建FTP服务器相关 编辑 Unity中与FTP相关的类 上传文件到FTP服务器 使用FTP服务器上传文件的关键点 开始上传 从FTP服务器下载文件到客户端 使用FTP下载文件的关键点 开始下载 关于FTP服务器的其他操作 将文件的上传,下载&…...
制作一款打飞机游戏教程8:抖动
我们讨论了爆炸效果,这是非常重要的内容。我们制作了一个可以改变大小的小圆点,并展示了一些微调,比如绘制的圆圈数量和颜色调整等。但我们也提到将要做一些重大改变,这些改变将涉及到颜色的使用方式。 颜色使用方式的改变 目前…...
Linux搭建环境:从零开始掌握基础操作(四)
您好,我是程序员小羊! 前言 软件测试第一步就是搭建测试环境,如何搭建好测试环境,需要具备两项的基础知识: 1、Linux 命令: 软件测试第一个任务, 一般都需要进行环境搭建, 一部分,环境搭建内容是在服…...
第2.4节:学会像AWK一样思考
1 第2.4节:学会像AWK一样思考 AWK的工作方式类似于工厂的流水线。文本数据就像流水线上的产品,AWK逐行读取这些文本,对每行文本进行分割处理,然后通过一系列的模式匹配和动作执行来完成特定的任务。下面我们详细介绍AWK的工作流程…...
内网穿透原理解析、使用网络场景、及如何实现公网访问步骤教程
不多废话,一文了解内网穿透原理解析、使用网络场景、及如何实现公网访问步骤教程。 一,内网穿透原理解析 内网穿透的核心原理是通过中间服务器端口数据转发或点到点技术建立端对端的直连通信通道,使外网设备能够访问内网设备和服务。 1&…...
购买电脑时,主要需要关注以下核心配置,它们直接影响性能、使用体验和价格。根据需求(办公、游戏、设计、编程等),侧重点会有所不同。看看Deepseek的建议
1. 处理器(CPU) 作用:电脑的“大脑”,影响整体运算速度和多任务处理能力。关键参数: 品牌与型号:Intel(酷睿i3/i5/i7/i9)或 AMD(锐龙R3/R5/R7/R9)。核心/线程…...
数据结构与算法[零基础]---4.树和二叉树
四、树和二叉树 (一)树 1.相关定义 树是由一个或多个结点组成的有限集T,它满足以下两个条件:第一个是有一个特定的结点,作为根结点;第二个其余的结点分成m(m>0)个互不相交的有限集T0,T1,.…...
Sklearn入门之数据预处理preprocessing
、 Sklearn全称:Scipy-toolkit Learn是 一个基于scipy实现的的开源机器学习库。它提供了大量的算法和工具,用于数据挖掘和数据分析,包括分类、回归、聚类等多种任务。本文我将带你了解并入门Sklearn下的preprocessing在机器学习中的基本用法。 获取方式…...
4.16学习总结 IO流综合练习
爬虫获取网站内的数据,获得完整姓名 网站一:姓氏 网站二:男生名字 网站三:女生名字 进行拼接,获取完整的男生女生姓名。 //导包 import org.apache.commons.io.FileUtils; import java.io.*; import java.io.IOEx…...
大模型全景解析:从技术突破到行业变革
目录 一、引言:人工智能的新纪元 二、大模型发展历史与技术演进 1. 早期探索期(2015-2017):从"人工智障"到初具规模 RNN/LSTM架构时代(2013-2017) Transformer革命(2017…...
充电宝项目中的MQTT(轻量高效的物联网通信协议)
文章目录 补充:HTTP协议MQTT协议MQTT的核心特性MQTT vs HTTP:关键对比 EMQX项目集成EMQX集成配置客户端和回调方法具体接口和方法处理处理类 补充:HTTP协议 HTTP是一种应用层协议,使用TCP作为传输层协议,默认端口是80…...
AgentOps - 帮助开发者构建、评估和监控 AI Agent
文章目录 一、关于 AgentOps二、关键集成 🔌三、快速开始 ⌨️2行代码中的Session replays 首类开发者体验 四、集成 🦾OpenAI Agents SDK 🖇️CrewAI 🛶AG2 🤖Camel AI 🐪Langchain 🦜…...
n8n 为技术团队打造的安全工作流自动化平台
AI MCP 系列 AgentGPT-01-入门介绍 Browser-use 是连接你的AI代理与浏览器的最简单方式 AI MCP(大模型上下文)-01-入门介绍 AI MCP(大模型上下文)-02-awesome-mcp-servers 精选的 MCP 服务器 AI MCP(大模型上下文)-03-open webui 介绍 是一个可扩展、功能丰富且用户友好的…...
MyBatis:SpringBoot结合MyBatis、MyBatis插件机制的原理分析与实战
🪁🍁 希望本文能给您带来帮助,如果有任何问题,欢迎批评指正!🐅🐾🍁🐥 文章目录 一、背景二、Spring Boot项目中结合MyBatis2.1 数据准备2.2 pom.xml依赖增加2.3 applicat…...
【数据结构】3.单链表专题
文章目录 单链表的实现0、准备工作1、链表的打印2、尾插3、头插4、尾删5、头删6、查找指定数据的位置7、在指定位置之前插入数据8、在指定位置之后插入数据9、删除指定位置的数据10、删除指定位置之后的数据11、单链表的销毁 单链表的实现 什么是单链表呢?单链表可…...
**Microsoft Certified Professional(MCP)** 认证考试
1. MCP 认证考试概述 MCP(Microsoft Certified Professional)是微软认证体系中的一项入门级认证,旨在验证考生在微软产品和技术(如 Windows Server、Azure、SQL Server、Microsoft 365)方面的技能。2020 年࿰…...
C++学习之游戏服务器开发git命令
目录 1.服务器需求分析 2.面向框架编程简介 3.ZINX框架初始 4.回显标准输入 5.VS结合GIT 6.完善readme范例 7.添加退出功能 8.添加命令处理类 9.添加日期前缀思路 10.添加日期前缀功能 1.服务器需求分析 zinx 描述 zinx 框架是一个处理多路 IO 的框架。在这个框架中提…...
Maven 多仓库与镜像配置全攻略:从原理到企业级实践
Maven 多仓库与镜像配置全攻略:从原理到企业级实践 一、核心概念:Repository 与 Mirror 的本质差异 在 Maven 依赖管理体系中,repository与mirror是构建可靠依赖解析链的两大核心组件,其核心区别如下: 1. Repositor…...
无锁队列--知识分享
目录 无锁队列 无锁队列是什么 为什么需要无锁队列 队列的类型 无锁队列的分类 ringbuffer(SPSC) ret_ring(MPMC) 无锁队列 无锁队列是什么 无锁队列通过原子操作来实现线程安全的队列,属于非阻塞队列 …...
Flask快速入门
1.安装 Flask 要使用 Flask,你需要先安装它。打开终端,运行以下命令: pip install flask 2.创建文件结构 3.app.py from flask import Flask:从 flask 库中导入 Flask 类。app Flask(__name__):创建一个 Flask 应…...
LeetCode -- Flora -- edit 2025-04-16
1.两数之和 1. 两数之和 给定一个整数数组 nums 和一个整数目标值 target,请你在该数组中找出 和为目标值 target 的那 两个 整数,并返回它们的数组下标。 你可以假设每种输入只会对应一个答案,并且你不能使用两次相同的元素。 你可以按…...
【Unity笔记】实现可视化配置的Unity按键输入管理器(按下/长按/松开事件 + UnityEvent绑定)
【Unity笔记】实现可视化配置的Unity按键输入管理器 适用于角色控制、技能触发的Unity按键输入系统,支持UnityEvent事件绑定、长按/松开监听与启用开关 一、引言 在 Unity 游戏开发中,处理键盘输入是最常见的交互方式之一。尤其是角色控制、技能释放、菜…...
SpringMVC学习(请求与响应。常见参数类型接收与响应。@RequestParam、@RequestBody的使用)(详细示例)
目录 一、请求与响应。(RequestMapping) (1)使用注解RequestMapping对业务模块区分。 StudentController。 TeacherController。 (2)Apifox请求与响应。 "/student/login"。 "/teacher/login"。 二、常见参数…...
springboot 切面拦截自定义注解
使用切面来拦截被该注解标记的方法 依赖 <dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-aop</artifactId> </dependency>1. 定义自定义注解 import java.lang.annotation.ElementType; imp…...
QT —— 信号和槽(自定义信号和槽函数)
QT —— 信号和槽(自定义信号和槽函数) 自定义信号和槽函数一、自定义信号函数规范1. 声明位置2. 返回值与实现3. 参数与重载 二、自定义槽函数规范1. 声明位置(不同版本差异)2. 返回值与实现3. 参数与重载 三、信号发射规范1. 基…...