今天我们说一种思想,前缀和思想.
我们就两种前缀和实例来说明.
1.一维前缀和
给定一堆数,找出任意区间的和.
话不多说看代码
public static void fun1() {
Scanner scan = new Scanner(System.in);
int count = scan.nextInt();
int[] sum = new int[count + 1];
int[] arr = new int[count + 1];
for (int i = 1; i <= count; i++) {
int num = scan.nextInt();
sum[i] = sum[i - 1] + num; // 前缀和实例
arr[i] = num;
}
System.out.println("请输入区间");
int start = scan.nextInt();
int end = scan.nextInt();
// 获取前缀和
System.out.println("sum= " + (sum[end] - sum[start - 1]));
}
sum[i] = sum[i - 1] + num; 这是核心代码
这个相信大家都看的懂
每一项的前面所有元素的和都存储在sum[i] 里面 , 我们如果要得出么个区间的值 可以这样引用
比如 a-b --> sum[b] - sum[a - 1]
2.二维前缀和
问题: 给定一个矩阵,如何得出和最大的子矩阵? 并给出给出子矩阵的开始位置和结束位置
// 二维前缀和
public static void fun2() {
Scanner scan = new Scanner(System.in);
int n = scan.nextInt(); // 矩阵的宽度
int m = scan.nextInt(); // 矩阵的长度
int[][] arr = new int[n + 1][m + 1];
int[][] sum = new int[n + 1][m + 1];
for (int i = 1; i <= n; i++) {
for (int j = 1; j <= m; j++) {
int num = scan.nextInt();
arr[i][j] = num;
// 求前缀和
sum[i][j] = sum[i - 1][j] + sum[i][j - 1] - sum[i - 1][j - 1] + arr[i][j];
}
}
// 求最大子矩阵 并左顶点坐标 和右顶点坐标
// 保存坐标
int[] posi = new int[5];
int max = Integer.MIN_VALUE;
// 下面两层循环表示 枚举所有起点
for (int i = 1; i <= n; i++) {
for (int j = 1; j <= m; j++) {
// 下面两层循环表示 从起点到arr[k][h] 所构成的矩阵 求和
for (int k = i; k <= n; k++) {
for (int h = j; h <= m; h++) {
// 画图可以得出此公式
int temp = sum[k][h] - sum[i - 1][h] - sum[k][j - 1] + sum[i - 1][j - 1];
if (temp > max) {
max = temp;
posi[1] = i;
posi[2] = j;
posi[3] = k;
posi[4] = h;
}
}
}
}
}
System.out.println("start:(" + posi[1] + "," + posi[2] + ")" + ", end:(" + posi[3] + "," + posi[4] + ")");
System.out.println("result: " + max);
}
第一个难点 保存前缀和到二维数组sum[][] 里
看图:
sum[i][j] = sum[i - 1][j] + sum[i][j - 1] - sum[i - 1][j - 1] + arr[i][j]
这个公式还是比较好理解的
再次回到这个问题,我们要寻找最大字矩阵,我们就需要枚举所有的可能性,没有前缀和之前我们需要六层for循环,有了前缀和之后,只需要四层就行了,
我们枚举每一个点,从每一个点向其他店扩展,就形成了以这个点为起点的所有矩阵,我们比较其大小即可,代码如上,稍微理解下都能想到