Skip to content

fix: update solutions to lc problem: No.2850 #3292

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 1 commit into from
Jul 20, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -180,6 +180,91 @@ class Solution {
}
```

<!-- tabs:end -->

<!-- solution:end -->

<!-- solution:start -->

### 方法二:状态压缩动态规划

我们可以把所有值为 $0$ 的单元格坐标 $(i, j)$ 放入数组 $left$ 中,如果单元格的值 $v$ 大于 $1$,那么我们把 $v-1$ 个坐标 $(i, j)$ 放入数组 $right$ 中。那么问题就转化为,每个 $right$ 中的坐标 $(i, j)$ 都要移动到 $left$ 中的一个坐标 $(x, y)$,求最少的移动次数。

我们记 $left$ 的长度为 $n$,那么我们可以使用 $n$ 位二进制数来表示 $left$ 中的每个坐标是否被 $right$ 中的坐标填充,其中 $1$ 表示被填充,而 $0$ 表示未被填充。初始时 $f[i] = \infty$,其余 $f[0]=0$。

考虑 $f[i]$,记当前 $i$ 的二进制表示中 $1$ 的个数为 $k$,我们在 $[0..n)$ 的范围内枚举 $j$,如果 $i$ 的第 $j$ 位为 $1$,那么 $f[i]$ 可以由 $f[i \oplus (1 << j)]$ 转移而来,转移的代价为 $cal(left[k-1], right[j])$,其中 $cal$ 表示两个坐标之间的曼哈顿距离。最终答案为 $f[(1 << n) - 1]$。

时间复杂度 $O(n \times 2^n)$,空间复杂度 $O(2^n)$。其中 $n$ 表示 $left$ 的长度,本题中 $n \le 9$。

<!-- tabs:start -->

#### Python3

```python
class Solution:
def minimumMoves(self, grid: List[List[int]]) -> int:
def cal(a: tuple, b: tuple) -> int:
return abs(a[0] - b[0]) + abs(a[1] - b[1])

left, right = [], []
for i in range(3):
for j in range(3):
if grid[i][j] == 0:
left.append((i, j))
else:
for _ in range(grid[i][j] - 1):
right.append((i, j))

n = len(left)
f = [inf] * (1 << n)
f[0] = 0
for i in range(1, 1 << n):
k = i.bit_count()
for j in range(n):
if i >> j & 1:
f[i] = min(f[i], f[i ^ (1 << j)] + cal(left[k - 1], right[j]))
return f[-1]
```

#### Java

```java
class Solution {
public int minimumMoves(int[][] grid) {
List<int[]> left = new ArrayList<>();
List<int[]> right = new ArrayList<>();
for (int i = 0; i < 3; ++i) {
for (int j = 0; j < 3; ++j) {
if (grid[i][j] == 0) {
left.add(new int[] {i, j});
} else {
for (int k = 1; k < grid[i][j]; ++k) {
right.add(new int[] {i, j});
}
}
}
}
int n = left.size();
int[] f = new int[1 << n];
Arrays.fill(f, 1 << 30);
f[0] = 0;
for (int i = 1; i < 1 << n; ++i) {
int k = Integer.bitCount(i);
for (int j = 0; j < n; ++j) {
if ((i >> j & 1) == 1) {
f[i] = Math.min(f[i], f[i ^ (1 << j)] + cal(left.get(k - 1), right.get(j)));
}
}
}
return f[(1 << n) - 1];
}

private int cal(int[] a, int[] b) {
return Math.abs(a[0] - b[0]) + Math.abs(a[1] - b[1]);
}
}
```

#### C++

```cpp
Expand Down Expand Up @@ -306,89 +391,4 @@ function minimumMoves(grid: number[][]): number {

<!-- solution:end -->

<!-- solution:start -->

### 方法二:状态压缩动态规划

我们可以把所有值为 $0$ 的单元格坐标 $(i, j)$ 放入数组 $left$ 中,如果单元格的值 $v$ 大于 $1$,那么我们把 $v-1$ 个坐标 $(i, j)$ 放入数组 $right$ 中。那么问题就转化为,每个 $right$ 中的坐标 $(i, j)$ 都要移动到 $left$ 中的一个坐标 $(x, y)$,求最少的移动次数。

我们记 $left$ 的长度为 $n$,那么我们可以使用 $n$ 位二进制数来表示 $left$ 中的每个坐标是否被 $right$ 中的坐标填充,其中 $1$ 表示被填充,而 $0$ 表示未被填充。初始时 $f[i] = \infty$,其余 $f[0]=0$。

考虑 $f[i]$,记当前 $i$ 的二进制表示中 $1$ 的个数为 $k$,我们在 $[0..n)$ 的范围内枚举 $j$,如果 $i$ 的第 $j$ 位为 $1$,那么 $f[i]$ 可以由 $f[i \oplus (1 << j)]$ 转移而来,转移的代价为 $cal(left[k-1], right[j])$,其中 $cal$ 表示两个坐标之间的曼哈顿距离。最终答案为 $f[(1 << n) - 1]$。

时间复杂度 $O(n \times 2^n)$,空间复杂度 $O(2^n)$。其中 $n$ 表示 $left$ 的长度,本题中 $n \le 9$。

<!-- tabs:start -->

#### Python3

```python
class Solution:
def minimumMoves(self, grid: List[List[int]]) -> int:
def cal(a: tuple, b: tuple) -> int:
return abs(a[0] - b[0]) + abs(a[1] - b[1])

left, right = [], []
for i in range(3):
for j in range(3):
if grid[i][j] == 0:
left.append((i, j))
else:
for _ in range(grid[i][j] - 1):
right.append((i, j))

n = len(left)
f = [inf] * (1 << n)
f[0] = 0
for i in range(1, 1 << n):
k = i.bit_count()
for j in range(n):
if i >> j & 1:
f[i] = min(f[i], f[i ^ (1 << j)] + cal(left[k - 1], right[j]))
return f[-1]
```

#### Java

```java
class Solution {
public int minimumMoves(int[][] grid) {
List<int[]> left = new ArrayList<>();
List<int[]> right = new ArrayList<>();
for (int i = 0; i < 3; ++i) {
for (int j = 0; j < 3; ++j) {
if (grid[i][j] == 0) {
left.add(new int[] {i, j});
} else {
for (int k = 1; k < grid[i][j]; ++k) {
right.add(new int[] {i, j});
}
}
}
}
int n = left.size();
int[] f = new int[1 << n];
Arrays.fill(f, 1 << 30);
f[0] = 0;
for (int i = 1; i < 1 << n; ++i) {
int k = Integer.bitCount(i);
for (int j = 0; j < n; ++j) {
if ((i >> j & 1) == 1) {
f[i] = Math.min(f[i], f[i ^ (1 << j)] + cal(left.get(k - 1), right.get(j)));
}
}
}
return f[(1 << n) - 1];
}

private int cal(int[] a, int[] b) {
return Math.abs(a[0] - b[0]) + Math.abs(a[1] - b[1]);
}
}
```

<!-- tabs:end -->

<!-- solution:end -->

<!-- problem:end -->
Original file line number Diff line number Diff line change
Expand Up @@ -174,6 +174,91 @@ class Solution {
}
```

<!-- tabs:end -->

<!-- solution:end -->

<!-- solution:start -->

### Solution 2: State Compression Dynamic Programming

We can put all the coordinates $(i, j)$ of cells with a value of $0$ into an array $left$. If the value $v$ of a cell is greater than $1$, we put $v-1$ coordinates $(i, j)$ into an array $right$. The problem then becomes that each coordinate $(i, j)$ in $right$ needs to be moved to a coordinate $(x, y)$ in $left$, and we need to find the minimum number of moves.

Let's denote the length of $left$ as $n$. We can use an $n$-bit binary number to represent whether each coordinate in $left$ is filled by a coordinate in $right$, where $1$ represents being filled, and $0$ represents not being filled. Initially, $f[i] = \infty$, and the rest $f[0]=0$.

Consider $f[i]$, let the number of $1$s in the binary representation of $i$ be $k$. We enumerate $j$ in the range $[0..n)$, if the $j$th bit of $i$ is $1$, then $f[i]$ can be transferred from $f[i \oplus (1 << j)]$, and the cost of the transfer is $cal(left[k-1], right[j])$, where $cal$ represents the Manhattan distance between two coordinates. The final answer is $f[(1 << n) - 1]$.

The time complexity is $O(n \times 2^n)$, and the space complexity is $O(2^n)$. Here, $n$ is the length of $left$, and in this problem, $n \le 9$.

<!-- tabs:start -->

#### Python3

```python
class Solution:
def minimumMoves(self, grid: List[List[int]]) -> int:
def cal(a: tuple, b: tuple) -> int:
return abs(a[0] - b[0]) + abs(a[1] - b[1])

left, right = [], []
for i in range(3):
for j in range(3):
if grid[i][j] == 0:
left.append((i, j))
else:
for _ in range(grid[i][j] - 1):
right.append((i, j))

n = len(left)
f = [inf] * (1 << n)
f[0] = 0
for i in range(1, 1 << n):
k = i.bit_count()
for j in range(n):
if i >> j & 1:
f[i] = min(f[i], f[i ^ (1 << j)] + cal(left[k - 1], right[j]))
return f[-1]
```

#### Java

```java
class Solution {
public int minimumMoves(int[][] grid) {
List<int[]> left = new ArrayList<>();
List<int[]> right = new ArrayList<>();
for (int i = 0; i < 3; ++i) {
for (int j = 0; j < 3; ++j) {
if (grid[i][j] == 0) {
left.add(new int[] {i, j});
} else {
for (int k = 1; k < grid[i][j]; ++k) {
right.add(new int[] {i, j});
}
}
}
}
int n = left.size();
int[] f = new int[1 << n];
Arrays.fill(f, 1 << 30);
f[0] = 0;
for (int i = 1; i < 1 << n; ++i) {
int k = Integer.bitCount(i);
for (int j = 0; j < n; ++j) {
if ((i >> j & 1) == 1) {
f[i] = Math.min(f[i], f[i ^ (1 << j)] + cal(left.get(k - 1), right.get(j)));
}
}
}
return f[(1 << n) - 1];
}

private int cal(int[] a, int[] b) {
return Math.abs(a[0] - b[0]) + Math.abs(a[1] - b[1]);
}
}
```

#### C++

```cpp
Expand Down Expand Up @@ -300,89 +385,4 @@ function minimumMoves(grid: number[][]): number {

<!-- solution:end -->

<!-- solution:start -->

### Solution 2: State Compression Dynamic Programming

We can put all the coordinates $(i, j)$ of cells with a value of $0$ into an array $left$. If the value $v$ of a cell is greater than $1$, we put $v-1$ coordinates $(i, j)$ into an array $right$. The problem then becomes that each coordinate $(i, j)$ in $right$ needs to be moved to a coordinate $(x, y)$ in $left$, and we need to find the minimum number of moves.

Let's denote the length of $left$ as $n$. We can use an $n$-bit binary number to represent whether each coordinate in $left$ is filled by a coordinate in $right$, where $1$ represents being filled, and $0$ represents not being filled. Initially, $f[i] = \infty$, and the rest $f[0]=0$.

Consider $f[i]$, let the number of $1$s in the binary representation of $i$ be $k$. We enumerate $j$ in the range $[0..n)$, if the $j$th bit of $i$ is $1$, then $f[i]$ can be transferred from $f[i \oplus (1 << j)]$, and the cost of the transfer is $cal(left[k-1], right[j])$, where $cal$ represents the Manhattan distance between two coordinates. The final answer is $f[(1 << n) - 1]$.

The time complexity is $O(n \times 2^n)$, and the space complexity is $O(2^n)$. Here, $n$ is the length of $left$, and in this problem, $n \le 9$.

<!-- tabs:start -->

#### Python3

```python
class Solution:
def minimumMoves(self, grid: List[List[int]]) -> int:
def cal(a: tuple, b: tuple) -> int:
return abs(a[0] - b[0]) + abs(a[1] - b[1])

left, right = [], []
for i in range(3):
for j in range(3):
if grid[i][j] == 0:
left.append((i, j))
else:
for _ in range(grid[i][j] - 1):
right.append((i, j))

n = len(left)
f = [inf] * (1 << n)
f[0] = 0
for i in range(1, 1 << n):
k = i.bit_count()
for j in range(n):
if i >> j & 1:
f[i] = min(f[i], f[i ^ (1 << j)] + cal(left[k - 1], right[j]))
return f[-1]
```

#### Java

```java
class Solution {
public int minimumMoves(int[][] grid) {
List<int[]> left = new ArrayList<>();
List<int[]> right = new ArrayList<>();
for (int i = 0; i < 3; ++i) {
for (int j = 0; j < 3; ++j) {
if (grid[i][j] == 0) {
left.add(new int[] {i, j});
} else {
for (int k = 1; k < grid[i][j]; ++k) {
right.add(new int[] {i, j});
}
}
}
}
int n = left.size();
int[] f = new int[1 << n];
Arrays.fill(f, 1 << 30);
f[0] = 0;
for (int i = 1; i < 1 << n; ++i) {
int k = Integer.bitCount(i);
for (int j = 0; j < n; ++j) {
if ((i >> j & 1) == 1) {
f[i] = Math.min(f[i], f[i ^ (1 << j)] + cal(left.get(k - 1), right.get(j)));
}
}
}
return f[(1 << n) - 1];
}

private int cal(int[] a, int[] b) {
return Math.abs(a[0] - b[0]) + Math.abs(a[1] - b[1]);
}
}
```

<!-- tabs:end -->

<!-- solution:end -->

<!-- problem:end -->
Loading