You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

212 lines
5.3 KiB

This file contains ambiguous Unicode characters!

This file contains ambiguous Unicode characters that may be confused with others in your current locale. If your use case is intentional and legitimate, you can safely ignore this warning. Use the Escape button to highlight these characters.

package class_2022_05_2_week;
// 来自网易
// 小红拿到了一个大立方体该大立方体由1*1*1的小方块拼成初始每个小方块都是白色。
// 小红可以每次选择一个小方块染成红色
// 每次小红可能选择同一个小方块重复染色
// 每次染色以后,你需要帮小红回答出当前的白色连通块数
// 如果两个小方块共用同一个面,且颜色相同,则它们是连通的
// 给定n、m、h表示大立方体的长、宽、高
// 给定k次操作每一次操作用(a, b, c)表示在大立方体的该位置进行染色
// 返回长度为k的数组表示每一次操作后白色方块的连通块数
// n * m * h <= 10 ^ 5k <= 10 ^ 5
public class Code05_RedAndWhiteSquares {
// 暴力方法
// 时间复杂度(k * n * m * h);
public static int[] blocks1(int n, int m, int h, int[][] ops) {
int k = ops.length;
int[][][] cube = new int[n][m][h];
int value = 1;
int[] ans = new int[k];
for (int i = 0; i < k; i++) {
cube[ops[i][0]][ops[i][1]][ops[i][2]] = -1;
for (int x = 0; x < n; x++) {
for (int y = 0; y < m; y++) {
for (int z = 0; z < h; z++) {
if (cube[x][y][z] != -1 && cube[x][y][z] != value) {
ans[i]++;
infect(cube, x, y, z, value);
}
}
}
}
value++;
}
return ans;
}
public static void infect(int[][][] cube, int a, int b, int c, int change) {
if (a < 0 || a == cube.length || b < 0 || b == cube[0].length || c < 0 || c == cube[0][0].length
|| cube[a][b][c] == -1 || cube[a][b][c] == change) {
return;
}
cube[a][b][c] = change;
infect(cube, a - 1, b, c, change);
infect(cube, a + 1, b, c, change);
infect(cube, a, b - 1, c, change);
infect(cube, a, b + 1, c, change);
infect(cube, a, b, c - 1, change);
infect(cube, a, b, c + 1, change);
}
// 最优解
// O(k + n * m * h)
public static int[] blocks2(int n, int m, int h, int[][] ops) {
int k = ops.length;
int[][][] red = new int[n][m][h];
for (int[] op : ops) {
red[op[0]][op[1]][op[2]]++;
}
UnionFind uf = new UnionFind(n, m, h, red);
int[] ans = new int[k];
for (int i = k - 1; i >= 0; i--) {
ans[i] = uf.sets;
int x = ops[i][0];
int y = ops[i][1];
int z = ops[i][2];
if (--red[x][y][z] == 0) {
// x, y ,z 这个格子,变白,建立自己的小集合
// 然后6个方向集合该合并合并
uf.finger(x, y, z);
}
}
return ans;
}
public static class UnionFind {
public int n;
public int m;
public int h;
public int[] father;
public int[] size;
public int[] help;
public int sets;
public UnionFind(int a, int b, int c, int[][][] red) {
n = a;
m = b;
h = c;
int len = n * m * h;
father = new int[len];
size = new int[len];
help = new int[len];
for (int x = 0; x < n; x++) {
for (int y = 0; y < m; y++) {
for (int z = 0; z < h; z++) {
if (red[x][y][z] == 0) {
finger(x, y, z);
}
}
}
}
}
public void finger(int x, int y, int z) {
// xyz
// 一维数值
int i = index(x, y, z);
father[i] = i;
size[i] = 1;
sets++;
union(i, x - 1, y, z);
union(i, x + 1, y, z);
union(i, x, y - 1, z);
union(i, x, y + 1, z);
union(i, x, y, z - 1);
union(i, x, y, z + 1);
}
private int index(int x, int y, int z) {
return z * n * m + y * n + x;
}
private void union(int i, int x, int y, int z) {
if (x < 0 || x == n || y < 0 || y == m || z < 0 || z == h) {
return;
}
int j = index(x, y, z);
if (size[j] == 0) {
return;
}
i = find(i);
j = find(j);
if (i != j) {
if (size[i] >= size[j]) {
father[j] = i;
size[i] += size[j];
} else {
father[i] = j;
size[j] += size[i];
}
sets--;
}
}
private int find(int i) {
int s = 0;
while (i != father[i]) {
help[s++] = i;
i = father[i];
}
while (s > 0) {
father[help[--s]] = i;
}
return i;
}
}
// 为了测试
public static int[][] randomOps(int n, int m, int h) {
int size = (int) (Math.random() * (n * m * h)) + 1;
int[][] ans = new int[size][3];
for (int i = 0; i < size; i++) {
ans[i][0] = (int) (Math.random() * n);
ans[i][1] = (int) (Math.random() * m);
ans[i][2] = (int) (Math.random() * h);
}
return ans;
}
// 为了测试
public static void main(String[] args) {
int size = 10;
int testTime = 5000;
System.out.println("测试开始");
for (int i = 0; i < testTime; i++) {
int n = (int) (Math.random() * size) + 1;
int m = (int) (Math.random() * size) + 1;
int h = (int) (Math.random() * size) + 1;
int[][] ops = randomOps(n, m, h);
int[] ans1 = blocks1(n, m, h, ops);
int[] ans2 = blocks2(n, m, h, ops);
for (int j = 0; j < ops.length; j++) {
if (ans1[j] != ans2[j]) {
System.out.println("出错了!");
}
}
}
System.out.println("测试结束");
// 立方体达到10^6规模
int n = 100;
int m = 100;
int h = 100;
int len = n * m * h;
// 操作条数达到10^6规模
int[][] ops = new int[len][3];
for (int i = 0; i < len; i++) {
ops[i][0] = (int) (Math.random() * n);
ops[i][1] = (int) (Math.random() * m);
ops[i][2] = (int) (Math.random() * h);
}
long start = System.currentTimeMillis();
blocks2(n, m, h, ops);
long end = System.currentTimeMillis();
System.out.println("运行时间(毫秒) : " + (end - start));
}
}