代码
import java.util.Arrays;
import java.util.Scanner;
public class Main {
public static void main(String[] args) {
/*
* 对n*n矩阵的 每个点 计算 r范围内和 与 t之间的关系
* 对于边界不全 为了方便计算 可对矩阵扩宽为 (n+2r)*(n+2r)的矩阵 其中补白值为t(求和与 定值大小判断)
* 其中 为了方便计算出每个 为(2r+1)矩阵块 的和(同全1矩阵之间的卷积)
* 可进行 行/列 之间的缩小计算:
* 可 先对于每行 进行 2r+1 滑动条和的缩小:进而此时(n+2r)*n 的行列式 每个值为 以该端点的行和
* 其次 对于列 在上述基础上进行相同操作 此时 n*n 的矩阵 每个值为该端点的行列之和
* 即可遍历n*n矩阵 对每个点进行判断即可
*/
int n, L, r, t;
Scanner scanner = new Scanner(System.in);
n = scanner.nextInt();
L = scanner.nextInt();
r = scanner.nextInt();
t = scanner.nextInt();
int[][] a = new int[n + 2 * r][n + 2 * r];// 原始矩阵 进行 补白 补充值为t
long sum = 0;//计算总和
for (int i = 0; i < n + 2 * r; i++) {
for (int j = 0; j < n + 2 * r; j++) {
if (i >= r && i <= r + n - 1 && j >= r && j <= r + n - 1) {
a[i][j] = scanner.nextInt();//非补值直接放入
sum += a[i][j];
} else {
a[i][j] = t;// 补入位置 补t 计算时将统一计算 均为全矩阵
}
}
}
if (n - 1 <= r) {// 判断矩阵只为一个 直接判断是否满足即可(和/平均值 都为一个)
//如果满足 则都满足 反之 则不然
if (sum <= t * n * n) {
System.out.print(n * n);
} else {
System.out.print(0);
}
scanner.close();
return;
}
int sum_tmp = 0;
// 先进行 列上的聚合 对每行进行滚动 2r+1 聚合 行数不变 列数成n
int[][] b = new int[n + 2 * r][n];
for (int i = 0; i < n + 2 * r; i++) {
sum_tmp = 0;
// 先计算每行的前缀和数组
int[] tmp = new int[n + 2 * r];
for (int j = 0; j < n + 2 * r; j++) {
sum_tmp += a[i][j];
tmp[j] = sum_tmp;
}
// 计算出 滚动结果数组 并放入
b[i][0] = tmp[2 * r];
for (int j = 1; j <= n - 1; j++) {
b[i][j] = tmp[2 * r + j] - tmp[j - 1];
}
}
// 再对上述已经 列聚合的数组 进行每列进行 2r+1 滚动 得到n*n 矩阵 矩阵的每一个值均为每个完全矩阵的sum
int[][] c = new int[n][n];
for (int i = 0; i < n; i++) {
sum_tmp = 0;
for(int j=0;j<2*r+1;j++) {
sum_tmp+=b[j][i];
}
for(int j = 0;j<n;j++) {
c[j][i] = sum_tmp;
if(j==n-1) {
break;
}
sum_tmp = sum_tmp - b[j][i] + b[2*r+1+j][i];
}
//
// sum_tmp = 0;
// int[] tmp = new int[n + 2 * r];
// for (int j = 0; j < n + 2 * r; j++) {
// sum_tmp += b[j][i];
// tmp[j] = sum_tmp;
// }
// c[0][i] = tmp[2 * r];
// for (int j = 1; j <= n - 1; j++) {
// c[j][i] = tmp[2 * r + j] - tmp[j - 1];
// }
}
int count = 0;
for (int i = 0; i < n; i++) {
for (int j = 0; j < n; j++) {
//求其和 t*(2r+1)^2 之间的关系 即为判断每个点邻域的均值是否小于t
if (c[i][j] <= Math.pow(2 * r + 1, 2) * t) {
count++;
}
}
}
//
// System.out.println();
// for (int i = 0; i < n + 2 * r; i++) {
// for (int j = 0; j < n + 2 * r; j++) {
// System.out.print(a[i][j] + " ");
// }
// System.out.print("\n");
// }
//
// System.out.println();
// for (int i = 0; i < n + 2 * r; i++) {
// for (int j = 0; j < n; j++) {
// System.out.print(b[i][j] + " ");
// }
// System.out.print("\n");
// }
//
// System.out.println();
// for (int i = 0; i < n; i++) {
// for (int j = 0; j < n; j++) {
// System.out.print(c[i][j] + " ");
// }
// System.out.print("\n");
// }
System.out.print(count);
scanner.close();
}
}
总结
1、当暴力明显无法通过全部数据时 需要使用 更好的方法减低时间复杂度
2、对于特殊的计算情况 思考能否人为转化为 统一计算方式 进而可以统一计算方法
3、思考时间复杂度主要集中的地方 如何优化
4、矩阵的计算方式 往往可以先考虑 行/列 进行 逐一计算/优化
5、本题 使用了 卷积神经网络的经典 方法 补0 其次对于每个t矩阵和 可通过行、列累计和 进而避免每个矩阵进行O(n^2)计算量
6、对于:n个数的一维数组,m为滚动窗口,可使用 对每次变化进行加减的方式进行移动计算m个连续和 或者 使用 前缀和数组 直接遍历计算出即可 均为O(n)