Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Sign up
Appearance settings

Commit 7687c2e

Browse files
committed
782-803-818-882-889-891-900 (7)
1 parent 9ce829e commit 7687c2e

File tree

15 files changed

+780
-54
lines changed

15 files changed

+780
-54
lines changed
Lines changed: 94 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,94 @@
1+
public class Solution782 {
2+
// 宫水三叶:构造分析题 https://leetcode.cn/problems/transform-to-chessboard/solutions/1769277/by-ac_oier-vf1m/
3+
static class V1 {
4+
private static final int INF = (int) 1e9;
5+
6+
public int movesToChessboard(int[][] board) {
7+
int n = board.length;
8+
int full = (1 << n) - 1;
9+
int r1 = -1, r2 = -1, c1 = -1, c2 = -1;
10+
for (int i = 0; i < n; i++) {
11+
int a = 0, b = 0;
12+
for (int j = 0; j < n; j++) {
13+
if (board[i][j] == 1) a += (1 << j);
14+
if (board[j][i] == 1) b += (1 << j);
15+
}
16+
if (r1 == -1) r1 = a;
17+
else if (r2 == -1 && a != r1) r2 = a;
18+
if (c1 == -1) c1 = b;
19+
else if (c2 == -1 && b != c1) c2 = b;
20+
if (a != r1 && a != r2) return -1;
21+
if (b != c1 && b != c2) return -1;
22+
}
23+
if (r2 == -1 || c2 == -1) return -1;
24+
if ((r1 ^ r2) != full || (c1 ^ c2) != full) return -1;
25+
int t = 0;
26+
for (int i = 0; i < n; i += 2) t += (1 << i);
27+
int ans = Math.min(getCnt(r1, t), getCnt(r2, t)) + Math.min(getCnt(c1, t), getCnt(c2, t));
28+
return ans >= INF ? -1 : ans;
29+
}
30+
31+
private int getCnt(int a, int b) {
32+
return Integer.bitCount(a) != Integer.bitCount(b) ? INF : Integer.bitCount(a ^ b) / 2;
33+
}
34+
}
35+
36+
// 灵茶山艾府:逆向思维 https://leetcode.cn/problems/transform-to-chessboard/solutions/2997293/tu-jie-ni-xiang-si-wei-pythonjavaccgojsr-mixb/
37+
static class V2 {
38+
public int movesToChessboard(int[][] board) {
39+
int n = board.length;
40+
int[] firstRow = board[0];
41+
int[] firstCol = new int[n];
42+
int[] rowCnt = new int[2];
43+
int[] colCnt = new int[2];
44+
for (int i = 0; i < n; i++) {
45+
rowCnt[firstRow[i]]++; // 统计 0 和 1 的个数
46+
firstCol[i] = board[i][0];
47+
colCnt[firstCol[i]]++;
48+
}
49+
// 第一行,0 和 1 的个数之差不能超过 1
50+
// 第一列,0 和 1 的个数之差不能超过 1
51+
if (Math.abs(rowCnt[0] - rowCnt[1]) > 1 || Math.abs(colCnt[0] - colCnt[1]) > 1) {
52+
return -1;
53+
}
54+
55+
// 每一行和第一行比较,要么完全相同,要么完全不同
56+
for (int[] row : board) {
57+
boolean same = row[0] == firstRow[0];
58+
for (int i = 0; i < n; i++) {
59+
if ((row[i] == firstRow[i]) != same) {
60+
return -1;
61+
}
62+
}
63+
}
64+
return minSwap(firstRow, rowCnt) + minSwap(firstCol, colCnt);
65+
}
66+
67+
// 计算最小交换次数
68+
private int minSwap(int[] s, int[] cnt) {
69+
int n = s.length;
70+
int x0 = cnt[1] > cnt[0] ? 1 : 0; // 如果 n 是偶数,x0 是 0
71+
int diff = 0;
72+
for (int i = 0; i < n; i++) {
73+
// 计算规则见题解正文最后一段,这里 t[i] = i % 2 ^ x0
74+
diff += s[i] ^ i % 2 ^ x0;
75+
}
76+
return n % 2 > 0 ? diff / 2 : Math.min(diff, n - diff) / 2;
77+
}
78+
}
79+
}
80+
/*
81+
782. 变为棋盘
82+
https://leetcode.cn/problems/transform-to-chessboard/description/
83+
84+
一个 n x n 的二维网络 board 仅由 0 和 1 组成 。每次移动,你能交换任意两列或是两行的位置。
85+
返回 将这个矩阵变为 "棋盘" 所需的最小移动次数 。如果不存在可行的变换,输出 -1。
86+
"棋盘" 是指任意一格的上下左右四个方向的值均与本身不同的矩阵。
87+
提示:
88+
n == board.length
89+
n == board[i].length
90+
2 <= n <= 30
91+
board[i][j] 将只包含 0或 1
92+
93+
时间复杂度 O(n^2)。
94+
*/
Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
import org.junit.jupiter.api.Assertions;
2+
import org.junit.jupiter.api.Test;
3+
4+
public class Solution782Tests {
5+
private final Solution782.V1 solution782_v1 = new Solution782.V1();
6+
private final Solution782.V2 solution782_v2 = new Solution782.V2();
7+
8+
@Test
9+
public void example1() {
10+
int[][] board = UtUtils.stringToInts2("[[0,1,1,0],[0,1,1,0],[1,0,0,1],[1,0,0,1]]");
11+
int expected = 2;
12+
Assertions.assertEquals(expected, solution782_v1.movesToChessboard(board));
13+
Assertions.assertEquals(expected, solution782_v2.movesToChessboard(board));
14+
}
15+
16+
@Test
17+
public void example2() {
18+
int[][] board = UtUtils.stringToInts2("[[0, 1], [1, 0]]");
19+
int expected = 0;
20+
Assertions.assertEquals(expected, solution782_v1.movesToChessboard(board));
21+
Assertions.assertEquals(expected, solution782_v2.movesToChessboard(board));
22+
}
23+
24+
@Test
25+
public void example3() {
26+
int[][] board = UtUtils.stringToInts2("[[1, 0], [1, 0]]");
27+
int expected = -1;
28+
Assertions.assertEquals(expected, solution782_v1.movesToChessboard(board));
29+
Assertions.assertEquals(expected, solution782_v2.movesToChessboard(board));
30+
}
31+
}
Lines changed: 145 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,145 @@
1+
import java.util.Arrays;
2+
3+
public class Solution803 {
4+
private static final int[][] DIRECTIONS = {{1, 0}, {0, 1}, {-1, 0}, {0, -1}};
5+
private int m, n;
6+
7+
public int[] hitBricks(int[][] grid, int[][] hits) {
8+
this.m = grid.length;
9+
this.n = grid[0].length;
10+
11+
// 第 1 步:把 grid 中的砖头全部击碎,通常算法问题不能修改输入数据,这一步非必需,可以认为是一种答题规范
12+
int[][] copy = new int[m][n];
13+
for (int i = 0; i < m; i++) {
14+
for (int j = 0; j < n; j++) {
15+
copy[i][j] = grid[i][j];
16+
}
17+
}
18+
19+
// 把 copy 中的砖头全部击碎
20+
for (int[] hit : hits) {
21+
copy[hit[0]][hit[1]] = 0;
22+
}
23+
24+
// 第 2 步:建图,把砖块和砖块的连接关系输入并查集,size 表示二维网格的大小,也表示虚拟的「屋顶」在并查集中的编号
25+
int size = m * n;
26+
DSU dsu = new DSU(size + 1);
27+
28+
// 将下标为 0 的这一行的砖块与「屋顶」相连
29+
for (int j = 0; j < n; j++) {
30+
if (copy[0][j] == 1) {
31+
dsu.union(j, size);
32+
}
33+
}
34+
35+
// 其余网格,如果是砖块向上、向左看一下,如果也是砖块,在并查集中进行合并
36+
for (int i = 1; i < m; i++) {
37+
for (int j = 0; j < n; j++) {
38+
if (copy[i][j] == 1) {
39+
// 如果上方也是砖块
40+
if (copy[i - 1][j] == 1) {
41+
dsu.union(getIndex(i - 1, j), getIndex(i, j));
42+
}
43+
// 如果左边也是砖块
44+
if (j > 0 && copy[i][j - 1] == 1) {
45+
dsu.union(getIndex(i, j - 1), getIndex(i, j));
46+
}
47+
}
48+
}
49+
}
50+
51+
// 第 3 步:按照 hits 的逆序,在 copy 中补回砖块,把每一次因为补回砖块而与屋顶相连的砖块的增量记录到 res 数组中
52+
int hitsLen = hits.length;
53+
int[] res = new int[hitsLen];
54+
for (int i = hitsLen - 1; i >= 0; i--) {
55+
int x = hits[i][0];
56+
int y = hits[i][1];
57+
58+
// 注意:这里不能用 copy,语义上表示,如果原来在 grid 中,这一块是空白,这一步不会产生任何砖块掉落
59+
// 逆向补回的时候,与屋顶相连的砖块数量也肯定不会增加
60+
if (grid[x][y] == 0) {
61+
continue;
62+
}
63+
64+
// 补回之前与屋顶相连的砖块数
65+
int origin = dsu.sz[dsu.find(size)];
66+
67+
// 注意:如果补回的这个结点在第 1 行,要告诉并查集它与屋顶相连(逻辑同第 2 步)
68+
if (x == 0) {
69+
dsu.union(y, size);
70+
}
71+
72+
// 在 4 个方向上看一下,如果相邻的 4 个方向有砖块,合并它们
73+
for (int[] d : DIRECTIONS) {
74+
int nx = x + d[0];
75+
int ny = y + d[1];
76+
77+
if (nx >= 0 && nx < m && ny >= 0 && ny < n && copy[nx][ny] == 1) {
78+
dsu.union(getIndex(x, y), getIndex(nx, ny));
79+
}
80+
}
81+
82+
// 补回之后与屋顶相连的砖块数
83+
int current = dsu.sz[dsu.find(size)];
84+
// 减去的 1 是逆向补回的砖块(正向移除的砖块),与 0 比较大小,是因为存在一种情况,添加当前砖块,不会使得与屋顶连接的砖块数更多
85+
res[i] = Math.max(0, current - origin - 1);
86+
87+
// 真正补上这个砖块
88+
copy[x][y] = 1;
89+
}
90+
return res;
91+
}
92+
93+
// 二维坐标转换为一维坐标
94+
private int getIndex(int x, int y) {
95+
return x * n + y;
96+
}
97+
98+
static class DSU {
99+
int[] fa;
100+
int[] sz;
101+
102+
public DSU(int n) {
103+
fa = new int[n];
104+
sz = new int[n];
105+
for (int i = 0; i < n; i++) fa[i] = i;
106+
Arrays.fill(sz, 1);
107+
}
108+
109+
int find(int x) { // 查找
110+
return x == fa[x] ? fa[x] : (fa[x] = find(fa[x]));
111+
}
112+
113+
void union(int p, int q) { // 合并
114+
p = find(p);
115+
q = find(q);
116+
if (p == q) return;
117+
fa[q] = p;
118+
sz[p] += sz[q];
119+
}
120+
}
121+
}
122+
/*
123+
803. 打砖块
124+
https://leetcode.cn/problems/bricks-falling-when-hit/description/
125+
126+
有一个 m x n 的二元网格 grid ,其中 1 表示砖块,0 表示空白。砖块 稳定(不会掉落)的前提是:
127+
- 一块砖直接连接到网格的顶部,或者
128+
- 至少有一块相邻(4 个方向之一)砖块 稳定 不会掉落时
129+
给你一个数组 hits ,这是需要依次消除砖块的位置。每当消除 hits[i] = (rowi, coli) 位置上的砖块时,对应位置的砖块(若存在)会消失,然后其他的砖块可能因为这一消除操作而 掉落 。一旦砖块掉落,它会 立即 从网格 grid 中消失(即,它不会落在其他稳定的砖块上)。
130+
返回一个数组 result ,其中 result[i] 表示第 i 次消除操作对应掉落的砖块数目。
131+
注意,消除可能指向是没有砖块的空白位置,如果发生这种情况,则没有砖块掉落。
132+
提示:
133+
m == grid.length
134+
n == grid[i].length
135+
1 <= m, n <= 200
136+
grid[i][j] 为 0 或 1
137+
1 <= hits.length <= 4 * 10^4
138+
hits[i].length == 2
139+
0 <= xi <= m - 1
140+
0 <= yi <= n - 1
141+
所有 (xi, yi) 互不相同
142+
143+
逆序思维 + 并查集 https://leetcode.cn/problems/bricks-falling-when-hit/solutions/561831/da-zhuan-kuai-by-leetcode-solution-szrq/
144+
时间复杂度 O(h * mn * log(mn))。其中 h = hits.length
145+
*/
Lines changed: 89 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,89 @@
1+
import java.util.Arrays;
2+
import java.util.Comparator;
3+
import java.util.PriorityQueue;
4+
5+
public class Solution818 {
6+
// dijkstra
7+
static class V1 {
8+
public int racecar(int target) {
9+
int k = 33 - Integer.numberOfLeadingZeros(target - 1);
10+
int barrier = 1 << k;
11+
12+
// dijkstra_mlogm
13+
int[] dist = new int[2 * barrier + 1];
14+
Arrays.fill(dist, Integer.MAX_VALUE);
15+
dist[target] = 0;
16+
// target, steps
17+
PriorityQueue<int[]> pq = new PriorityQueue<>(Comparator.comparingInt(a -> a[1]));
18+
pq.add(new int[]{target, 0});
19+
while (!pq.isEmpty()) {
20+
int[] top = pq.remove();
21+
int target1 = top[0], steps1 = top[1];
22+
int y = Math.floorMod(target1, dist.length); //
23+
if (dist[y] > steps1) continue;
24+
25+
for (int j = 0; j <= k; ++j) {
26+
int walk = (1 << j) - 1;
27+
int target2 = walk - target1;
28+
int steps2 = steps1 + j + (target2 != 0 ? 1 : 0);
29+
30+
// -3 % 17 = -3, Math.floorMod(-3, 17) = 14
31+
int z = Math.floorMod(target2, dist.length);
32+
if (Math.abs(target2) <= barrier && steps2 < dist[z]) {
33+
dist[z] = steps2;
34+
pq.add(new int[]{target2, steps2});
35+
}
36+
}
37+
}
38+
return dist[0];
39+
}
40+
}
41+
42+
// 动态规划
43+
static class V2 {
44+
public int racecar(int target) {
45+
// dp[x] 表示到达位置 x 的最短指令长度
46+
int[] dp = new int[target + 3];
47+
Arrays.fill(dp, Integer.MAX_VALUE);
48+
dp[0] = 0;
49+
dp[1] = 1;
50+
dp[2] = 4;
51+
52+
for (int t = 3; t <= target; t++) {
53+
int k = 32 - Integer.numberOfLeadingZeros(t);
54+
int full = (1 << k) - 1;
55+
if (t == full) {
56+
dp[t] = k;
57+
continue;
58+
}
59+
for (int j = 0; j < k - 1; ++j) {
60+
dp[t] = Math.min(dp[t], dp[t - (1 << (k - 1)) + (1 << j)] + k - 1 + j + 2);
61+
}
62+
if ((1 << k) - 1 - t < t) {
63+
dp[t] = Math.min(dp[t], dp[(1 << k) - 1 - t] + k + 1);
64+
}
65+
}
66+
return dp[target];
67+
}
68+
}
69+
}
70+
/*
71+
818. 赛车
72+
https://leetcode.cn/problems/race-car/description/
73+
74+
你的赛车可以从位置 0 开始,并且速度为 +1 ,在一条无限长的数轴上行驶。赛车也可以向负方向行驶。赛车可以按照由加速指令 'A' 和倒车指令 'R' 组成的指令序列自动行驶。
75+
- 当收到指令 'A' 时,赛车这样行驶:
76+
- position += speed
77+
- speed *= 2
78+
- 当收到指令 'R' 时,赛车这样行驶:
79+
- 如果速度为正数,那么speed = -1
80+
- 否则 speed = 1
81+
当前所处位置不变。
82+
例如,在执行指令 "AAR" 后,赛车位置变化为 0 --> 1 --> 3 --> 3 ,速度变化为 1 --> 2 --> 4 --> -1 。
83+
给你一个目标位置 target ,返回能到达目标位置的最短指令序列的长度。
84+
提示:
85+
1 <= target <= 10^4
86+
87+
dijkstra / 动态规划 https://leetcode.cn/problems/race-car/solutions/38854/sai-che-by-leetcode/
88+
时间复杂度 O(TlogT)。其中 O(T) 表示 barrier 的数量级
89+
*/

0 commit comments

Comments
(0)

AltStyle によって変換されたページ (->オリジナル) /