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.

293 lines
7.5 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_09_4_week;
import java.util.Arrays;
// 来自华为
// 给定一个二维数组map代表一个餐厅其中只有0、1两种值
// map[i][j] == 0 表示(i,j)位置是空座
// map[i][j] == 1 表示(i,j)位置坐了人
// 根据防疫要求,任何人的上、下、左、右,四个相邻的方向都不能再坐人
// 但是为了餐厅利用的最大化,也许还能在不违反防疫要求的情况下,继续安排人吃饭
// 请返回还能安排的最大人数
// 如果一开始的状况已经不合法,直接返回-1
// 比如:
// 1 0 0 0
// 0 0 0 1
// 不违反防疫要求的情况下这个餐厅最多还能安排2人如下所示X是新安排的人
// 1 0 X 0
// 0 X 0 1
// 再比如:
// 1 0 0 0 0 1
// 0 0 0 0 0 0
// 0 1 0 0 0 1
// 0 0 0 0 0 0
// 不违反防疫要求的情况下这个餐厅最多还能安排7人如下所示X是新安排的人
// 1 0 0 X 0 1
// 0 0 X 0 X 0
// 0 1 0 X 0 1
// X 0 X 0 X 0
// 数据范围 : 1 <= 矩阵的行、列 <= 20
public class Code03_MostSeats {
// public static int maxPeople(int[][] matrix) {
// int n = matrix.length;
// int m = matrix[0].length;
// for (int i = 0; i < n; i++) {
// for (int j = 0; j < m; j++) {
// if (matrix[i][j] == 1) {
// if (i > 0 && matrix[i - 1][j] == 1) {
// return -1;
// }
// if (i < n - 1 && matrix[i + 1][j] == 1) {
// return -1;
// }
// if (j > 0 && matrix[i][j - 1] == 1) {
// return -1;
// }
// if (j < m - 1 && matrix[i][j + 1] == 1) {
// return -1;
// }
// }
// }
// }
// int[] arr = new int[n];
// for (int i = 0; i < n; i++) {
// int status = 0;
// for (int j = 0; j < m; j++) {
// if (matrix[i][j] == 1) {
// status |= 1 << j;
// }
//
// arr[i] = status;
// }
// }
// return zuo(arr, n, m, 0, 0, 0);
//
// }
//
// public static int zuo(int[] arr, int n, int m, int i, int j, int status) {
// if (i == n) {
// return 0;
// }
// if (j == m) {
// return zuo(arr, n, m, i + 1, 0, status);
// }
// // 怎么判断已经无效了
// if ((arr[i] & status) != 0) {
// return -1;
// }
// // (i,j)
// int left = status(status, j - 1, m);
// int up = status(status, j, m);
// int right = status(arr[i], j + 1, m);
// int cur = status(arr[i], j, m);
// // 当前位置不安排新人了
// int p1 = -1;
// if (cur == 0) { // 原始的该位置无人
// int nextStatus = status ^ (up << j);
// p1 = zuo(arr, n, m, i, j + 1, nextStatus);
// } else { // 原始的该位置有人
// int nextStatus = (status | (1 << j));
// p1 = zuo(arr, n, m, i, j + 1, nextStatus);
// }
// // 当前位置安排新人
// int p2 = -1;
// if(left == 0 && up == 0 && right == 0 && cur == 0) {
// int nextStatus = (status | (1 << j));
// int next = zuo(arr, n, m, i, j + 1, nextStatus);
// if(next != -1) {
// p2 = 1 + next;
// }
// }
// return Math.max(p1, p2);
// }
// 为了测试,普通方法
// 普通的状态压缩动态规划
// 每一行用dfs的方法
// 体系学习班章节44 : 状态压缩的动态规划,贴瓷砖问题类似
public static int mostSeats1(int[][] map) {
int n = map.length;
int m = map[0].length;
int[] arr = new int[n];
for (int row = 0; row < n; row++) {
int status = 0;
for (int col = 0, i = m - 1; col < m; col++, i--) {
if (map[row][col] == 1) {
if (row > 0 && map[row - 1][col] == 1) {
return -1;
}
if (col > 0 && map[row][col - 1] == 1) {
return -1;
}
}
status |= map[row][col] << i;
}
arr[row] = status;
}
int[][] dp = new int[n][1 << m];
for (int i = 0; i < n; i++) {
Arrays.fill(dp[i], -2);
}
int ans = process1(arr, 0, 0, m, dp);
return ans == -1 ? 0 : ans;
}
public static int process1(int[] arr, int row, int pre, int m, int[][] dp) {
if (row == arr.length) {
return 0;
}
if (dp[row][pre] != -2) {
return dp[row][pre];
}
int cur = arr[row];
int ans = 0;
if ((cur & pre) != 0) {
ans = -1;
} else {
ans = dfs(arr, row, m - 1, pre, cur, m, dp);
}
dp[row][pre] = ans;
return ans;
}
public static int dfs(int[] arr, int row, int col, int pre, int seats, int m, int[][] dp) {
if (col == -1) {
return process1(arr, row + 1, seats, m, dp);
} else {
int p1 = dfs(arr, row, col - 1, pre, seats, m, dp);
int p2 = -1;
if ((pre & (1 << col)) == 0 && (seats & (1 << col)) == 0
&& (col == m - 1 || (seats & (1 << (col + 1))) == 0)
&& (col == 0 || (seats & (1 << (col - 1))) == 0)) {
int next2 = dfs(arr, row, col - 1, pre, seats | (1 << col), m, dp);
if (next2 != -1) {
p2 = 1 + next2;
}
}
return Math.max(p1, p2);
}
}
// 正式方法
// 轮廓线dp
public static int mostSeats2(int[][] map) {
int n = map.length;
int m = map[0].length;
int[] arr = new int[n];
for (int i = 0; i < n; i++) {
int status = 0;
for (int j = 0; j < m; j++) {
if (map[i][j] == 1) {
if (i > 0 && map[i - 1][j] == 1) {
return -1;
}
if (j > 0 && map[i][j - 1] == 1) {
return -1;
}
}
status |= map[i][j] << j;
}
arr[i] = status;
}
int s = 1 << m;
int[][][] dp = new int[n][m][s];
for (int i = 0; i < n; i++) {
for (int j = 0; j < m; j++) {
for (int k = 0; k < s; k++) {
dp[i][j][k] = -2;
}
}
}
int ans = process2(arr, n, m, 0, 0, 0, dp);
return ans == -1 ? 0 : ans;
}
// 20 * 20 * 2^20 -> 4 * 10^8
public static int process2(int[] arr, int n, int m, int i, int j, int status, int[][][] dp) {
if (j == m) {
return process2(arr, n, m, i + 1, 0, status, dp);
}
if (i == n) {
return 0;
}
if (dp[i][j][status] != -2) {
return dp[i][j][status];
}
int left = status(status, j - 1, m);
int up = status(status, j, m);
int cur = status(arr[i], j, m);
int right = status(arr[i], j + 1, m);
if (up == 1 && cur == 1) {
return -1;
}
int p1 = -1;
if (cur == 1) {
p1 = process2(arr, n, m, i, j + 1, status | (1 << j), dp);
} else {
p1 = process2(arr, n, m, i, j + 1, (status | (1 << j)) ^ (1 << j), dp);
}
int p2 = -1;
if (left == 0 && up == 0 && cur == 0 && right == 0) {
int next2 = process2(arr, n, m, i, j + 1, status | (1 << j), dp);
if (next2 != -1) {
p2 = 1 + next2;
}
}
int ans = Math.max(p1, p2);
dp[i][j][status] = ans;
return ans;
}
public static int status(int status, int i, int m) {
return (i < 0 || i == m || (status & (1 << i)) == 0) ? 0 : 1;
}
public static int[][] randomMatrix(int n, int m, double oneP) {
int[][] ans = new int[n][m];
for (int i = 0; i < n; i++) {
for (int j = 0; j < m; j++) {
ans[i][j] = Math.random() < oneP ? 1 : 0;
}
}
return ans;
}
public static void main(String[] args) {
int N = 10;
int M = 10;
// 产生1的概率
double oneP = 0.15;
int testTimes = 10000;
System.out.println("测试开始");
for (int i = 0; i < testTimes; i++) {
int n = (int) (Math.random() * N) + 1;
int m = (int) (Math.random() * M) + 1;
int[][] map = randomMatrix(n, m, oneP);
int ans1 = mostSeats1(map);
int ans2 = mostSeats2(map);
if (ans1 != ans2) {
for (int[] arr : map) {
for (int num : arr) {
System.out.print(num + " ");
}
System.out.println();
}
System.out.println(ans1);
System.out.println(ans2);
break;
}
}
System.out.println("测试结束");
int n = 20;
int[][] map = new int[n][n];
System.out.println("最大规模 : " + n + " * " + n);
long start = System.currentTimeMillis();
mostSeats2(map);
long end = System.currentTimeMillis();
System.out.println("运行时间 : " + (end - start) + " 毫秒");
}
}