Skip to content

Commit

Permalink
feat: add solutions to lc problem: No.3343 (#3710)
Browse files Browse the repository at this point in the history
No.3343.Count Number of Balanced Permutations
  • Loading branch information
yanglbme authored Nov 4, 2024
1 parent 7f9a026 commit fa38c56
Show file tree
Hide file tree
Showing 11 changed files with 860 additions and 12 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -84,7 +84,15 @@ edit_url: https://github.com/doocs/leetcode/edit/main/solution/3300-3399/3341.Fi

<!-- solution:start -->

### 方法一
### 方法一:Dijkstra 算法

我们定义一个二维数组 $\textit{dist}$,其中 $\textit{dist}[i][j]$ 表示从起点到达房间 $(i, j)$ 所需的最少时间。初始时,我们将 $\textit{dist}$ 数组中的所有元素设为无穷大,然后将起点 $(0, 0)$ 的 $\textit{dist}$ 值设为 $0$。

我们使用优先队列 $\textit{pq}$ 存储每一个状态,其中每个状态由三个值 $(d, i, j)$ 组成,表示从起点到达房间 $(i, j)$ 所需的时间为 $d$。初始时,我们将起点 $(0, 0, 0)$ 加入到 $\textit{pq}$ 中。

在每一次迭代中,我们取出 $\textit{pq}$ 中的队首元素 $(d, i, j)$,如果 $(i, j)$ 是终点,那么我们返回 $d$。如果 $d$ 大于 $\textit{dist}[i][j]$,那么我们跳过这个状态。否则,我们枚举 $(i, j)$ 的四个相邻位置 $(x, y)$,如果 $(x, y)$ 在地图内,那么我们计算从 $(i, j)$ 到 $(x, y)$ 的最终时间 $t = \max(\textit{moveTime}[x][y], \textit{dist}[i][j]) + 1$,如果 $t$ 小于 $\textit{dist}[x][y]$,那么我们更新 $\textit{dist}[x][y]$ 的值,并将 $(t, x, y)$ 加入到 $\textit{pq}$ 中。

时间复杂度 $O(n \times m \times \log (n \times m))$,空间复杂度 $O(n \times m)$。其中 $n$ 和 $m$ 分别是地图的行数和列数。

<!-- tabs:start -->

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -81,7 +81,15 @@ edit_url: https://github.com/doocs/leetcode/edit/main/solution/3300-3399/3341.Fi

<!-- solution:start -->

### Solution 1
### Solution 1: Dijkstra's Algorithm

We define a two-dimensional array $\textit{dist}$, where $\textit{dist}[i][j]$ represents the minimum time required to reach room $(i, j)$ from the starting point. Initially, we set all elements in the $\textit{dist}$ array to infinity, and then set the $\textit{dist}$ value of the starting point $(0, 0)$ to $0$.

We use a priority queue $\textit{pq}$ to store each state, where each state consists of three values $(d, i, j)$, representing the time $d$ required to reach room $(i, j)$ from the starting point. Initially, we add the starting point $(0, 0, 0)$ to $\textit{pq}$.

In each iteration, we take the front element $(d, i, j)$ from $\textit{pq}$. If $(i, j)$ is the endpoint, we return $d$. If $d$ is greater than $\textit{dist}[i][j]$, we skip this state. Otherwise, we enumerate the four adjacent positions $(x, y)$ of $(i, j)$. If $(x, y)$ is within the map, we calculate the final time $t$ from $(i, j)$ to $(x, y)$ as $t = \max(\textit{moveTime}[x][y], \textit{dist}[i][j]) + 1$. If $t$ is less than $\textit{dist}[x][y]$, we update the value of $\textit{dist}[x][y]$ and add $(t, x, y)$ to $\textit{pq}$.

The time complexity is $O(n \times m \times \log (n \times m))$, and the space complexity is $O(n \times m)$. Here, $n$ and $m$ are the number of rows and columns of the map, respectively.

<!-- tabs:start -->

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -85,7 +85,15 @@ edit_url: https://github.com/doocs/leetcode/edit/main/solution/3300-3399/3342.Fi

<!-- solution:start -->

### 方法一
### 方法一:Dijkstra 算法

我们定义一个二维数组 $\textit{dist}$,其中 $\textit{dist}[i][j]$ 表示从起点到达房间 $(i, j)$ 所需的最少时间。初始时,我们将 $\textit{dist}$ 数组中的所有元素设为无穷大,然后将起点 $(0, 0)$ 的 $\textit{dist}$ 值设为 $0$。

我们使用优先队列 $\textit{pq}$ 存储每一个状态,其中每个状态由三个值 $(d, i, j)$ 组成,表示从起点到达房间 $(i, j)$ 所需的时间为 $d$。初始时,我们将起点 $(0, 0, 0)$ 加入到 $\textit{pq}$ 中。

在每一次迭代中,我们取出 $\textit{pq}$ 中的队首元素 $(d, i, j)$,如果 $(i, j)$ 是终点,那么我们返回 $d$。如果 $d$ 大于 $\textit{dist}[i][j]$,那么我们跳过这个状态。否则,我们枚举 $(i, j)$ 的四个相邻位置 $(x, y)$,如果 $(x, y)$ 在地图内,那么我们计算从 $(i, j)$ 到 $(x, y)$ 的最终时间 $t = \max(\textit{moveTime}[x][y], \textit{dist}[i][j]) + (i + 2) \bmod 2 + 1$,如果 $t$ 小于 $\textit{dist}[x][y]$,那么我们更新 $\textit{dist}[x][y]$ 的值,并将 $(t, x, y)$ 加入到 $\textit{pq}$ 中。

时间复杂度 $O(n \times m \times \log (n \times m))$,空间复杂度 $O(n \times m)$。其中 $n$ 和 $m$ 分别是地图的行数和列数。

<!-- tabs:start -->

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -82,7 +82,15 @@ edit_url: https://github.com/doocs/leetcode/edit/main/solution/3300-3399/3342.Fi

<!-- solution:start -->

### Solution 1
### Solution 1: Dijkstra's Algorithm

We define a two-dimensional array $\textit{dist}$, where $\textit{dist}[i][j]$ represents the minimum time required to reach room $(i, j)$ from the starting point. Initially, we set all elements in the $\textit{dist}$ array to infinity, and then set the $\textit{dist}$ value of the starting point $(0, 0)$ to $0$.

We use a priority queue $\textit{pq}$ to store each state, where each state consists of three values $(d, i, j)$, representing the time $d$ required to reach room $(i, j)$ from the starting point. Initially, we add the starting point $(0, 0, 0)$ to $\textit{pq}$.

In each iteration, we take the front element $(d, i, j)$ from $\textit{pq}$. If $(i, j)$ is the endpoint, we return $d$. If $d$ is greater than $\textit{dist}[i][j]$, we skip this state. Otherwise, we enumerate the four adjacent positions $(x, y)$ of $(i, j)$. If $(x, y)$ is within the map, we calculate the final time $t$ from $(i, j)$ to $(x, y)$ as $t = \max(\textit{moveTime}[x][y], \textit{dist}[i][j]) + (i + j) \bmod 2 + 1$. If $t$ is less than $\textit{dist}[x][y]$, we update the value of $\textit{dist}[x][y]$ and add $(t, x, y)$ to $\textit{pq}$.

The time complexity is $O(n \times m \times \log (n \times m))$, and the space complexity is $O(n \times m)$. Here, $n$ and $m$ are the number of rows and columns of the map, respectively.

<!-- tabs:start -->

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -85,32 +85,309 @@ edit_url: https://github.com/doocs/leetcode/edit/main/solution/3300-3399/3343.Co

<!-- solution:start -->

### 方法一
### 方法一:记忆化搜索 + 组合数学

我们首先统计出字符串 $\textit{num}$ 中每个数字出现的次数,记录在数组 $\textit{cnt}$ 中,然后计算出字符串 $\textit{num}$ 的总和 $\textit{s}$。

如果 $\textit{s}$ 是奇数,那么 $\textit{num}$ 一定不是平衡的,直接返回 $0$。

接下来,我们定义记忆化搜索函数 $\text{dfs}(i, j, a, b)$,其中 $i$ 表示当前要从数字 $i$ 开始填充,而 $j$ 表示奇数位剩余待填的数字之和,而 $a$ 和 $b$ 分别表示奇数位和偶数位剩余待填的数字个数。我们记字符串 $\textit{num}$ 的长度为 $n$,那么答案就是 $\text{dfs}(0, s / 2, n / 2, (n + 1) / 2)$。

在 $\text{dfs}(i, j, a, b)$ 函数中,我们首先判断是否已经填充完了所有的数字,如果是的话,此时需要满足 $j = 0$ 且 $a = 0$ 且 $b = 0$,若满足这个条件,说明当前的排列是平衡的,返回 $1$,否则返回 $0$。

接下来,我们判断当前奇数位剩余待填的数字个数 $a$ 是否为 $0$ 且 $j > 0$,如果是的话,说明当前的排列不是平衡的,提前返回 $0$。

否则,我们可以枚举当前数字分配给奇数位的数字个数 $l$,那么偶数位的数字个数就是 $r = \textit{cnt}[i] - l$,我们需要满足 $0 \leq r \leq b$ 且 $l \times i \leq j$,然后我们计算出当前的方案数 $t = C_a^l \times C_b^r \times \text{dfs}(i + 1, j - l \times i, a - l, b - r)$,最后答案就是所有方案数之和。

时间复杂度 $O(|\Sigma| \times n^2 \times (n + |\Sigma|))$,其中 $|\Sigma|$ 表示数字的种类数,本题中 $|\Sigma| = 10$。空间复杂度 $O(n^2 \times |\Sigma|^2)$。

<!-- tabs:start -->

#### Python3

```python

class Solution:
def countBalancedPermutations(self, num: str) -> int:
@cache
def dfs(i: int, j: int, a: int, b: int) -> int:
if i > 9:
return (j | a | b) == 0
if a == 0 and j:
return 0
ans = 0
for l in range(min(cnt[i], a) + 1):
r = cnt[i] - l
if 0 <= r <= b and l * i <= j:
t = comb(a, l) * comb(b, r) * dfs(i + 1, j - l * i, a - l, b - r)
ans = (ans + t) % mod
return ans

nums = list(map(int, num))
s = sum(nums)
if s % 2:
return 0
n = len(nums)
mod = 10**9 + 7
cnt = Counter(nums)
return dfs(0, s // 2, n // 2, (n + 1) // 2)
```

#### Java

```java

class Solution {
private final int[] cnt = new int[10];
private final int mod = (int) 1e9 + 7;
private Integer[][][][] f;
private long[][] c;

public int countBalancedPermutations(String num) {
int s = 0;
for (char c : num.toCharArray()) {
cnt[c - '0']++;
s += c - '0';
}
if (s % 2 == 1) {
return 0;
}
int n = num.length();
int m = n / 2 + 1;
f = new Integer[10][s / 2 + 1][m][m + 1];
c = new long[m + 1][m + 1];
c[0][0] = 1;
for (int i = 1; i <= m; i++) {
c[i][0] = 1;
for (int j = 1; j <= i; j++) {
c[i][j] = (c[i - 1][j] + c[i - 1][j - 1]) % mod;
}
}
return dfs(0, s / 2, n / 2, (n + 1) / 2);
}

private int dfs(int i, int j, int a, int b) {
if (i > 9) {
return ((j | a | b) == 0) ? 1 : 0;
}
if (a == 0 && j != 0) {
return 0;
}
if (f[i][j][a][b] != null) {
return f[i][j][a][b];
}
int ans = 0;
for (int l = 0; l <= Math.min(cnt[i], a); ++l) {
int r = cnt[i] - l;
if (r >= 0 && r <= b && l * i <= j) {
int t = (int) (c[a][l] * c[b][r] % mod * dfs(i + 1, j - l * i, a - l, b - r) % mod);
ans = (ans + t) % mod;
}
}
return f[i][j][a][b] = ans;
}
}
```

#### C++

```cpp

using ll = long long;
const int MX = 80;
const int MOD = 1e9 + 7;
ll c[MX][MX];

auto init = [] {
c[0][0] = 1;
for (int i = 1; i < MX; ++i) {
c[i][0] = 1;
for (int j = 1; j <= i; ++j) {
c[i][j] = (c[i - 1][j] + c[i - 1][j - 1]) % MOD;
}
}
return 0;
}();

class Solution {
public:
int countBalancedPermutations(string num) {
int cnt[10]{};
int s = 0;
for (char& c : num) {
++cnt[c - '0'];
s += c - '0';
}
if (s % 2) {
return 0;
}
int n = num.size();
int m = n / 2 + 1;
int f[10][s / 2 + 1][m][m + 1];
memset(f, -1, sizeof(f));
auto dfs = [&](auto&& dfs, int i, int j, int a, int b) -> int {
if (i > 9) {
return ((j | a | b) == 0 ? 1 : 0);
}
if (a == 0 && j) {
return 0;
}
if (f[i][j][a][b] != -1) {
return f[i][j][a][b];
}
int ans = 0;
for (int l = 0; l <= min(cnt[i], a); ++l) {
int r = cnt[i] - l;
if (r >= 0 && r <= b && l * i <= j) {
int t = c[a][l] * c[b][r] % MOD * dfs(dfs, i + 1, j - l * i, a - l, b - r) % MOD;
ans = (ans + t) % MOD;
}
}
return f[i][j][a][b] = ans;
};
return dfs(dfs, 0, s / 2, n / 2, (n + 1) / 2);
}
};
```
#### Go
```go
const (
MX = 80
MOD = 1_000_000_007
)
var c [MX][MX]int
func init() {
c[0][0] = 1
for i := 1; i < MX; i++ {
c[i][0] = 1
for j := 1; j <= i; j++ {
c[i][j] = (c[i-1][j] + c[i-1][j-1]) % MOD
}
}
}
func countBalancedPermutations(num string) int {
var cnt [10]int
s := 0
for _, ch := range num {
cnt[ch-'0']++
s += int(ch - '0')
}
if s%2 != 0 {
return 0
}
n := len(num)
m := n/2 + 1
f := make([][][][]int, 10)
for i := range f {
f[i] = make([][][]int, s/2+1)
for j := range f[i] {
f[i][j] = make([][]int, m)
for k := range f[i][j] {
f[i][j][k] = make([]int, m+1)
for l := range f[i][j][k] {
f[i][j][k][l] = -1
}
}
}
}
var dfs func(i, j, a, b int) int
dfs = func(i, j, a, b int) int {
if i > 9 {
if j == 0 && a == 0 && b == 0 {
return 1
}
return 0
}
if a == 0 && j > 0 {
return 0
}
if f[i][j][a][b] != -1 {
return f[i][j][a][b]
}
ans := 0
for l := 0; l <= min(cnt[i], a); l++ {
r := cnt[i] - l
if r >= 0 && r <= b && l*i <= j {
t := c[a][l] * c[b][r] % MOD * dfs(i+1, j-l*i, a-l, b-r) % MOD
ans = (ans + t) % MOD
}
}
f[i][j][a][b] = ans
return ans
}
return dfs(0, s/2, n/2, (n+1)/2)
}
```

#### TypeScript

```ts
const MX = 80;
const MOD = 10 ** 9 + 7;
const c: number[][] = Array.from({ length: MX }, () => Array(MX).fill(0));
(function init() {
c[0][0] = 1;
for (let i = 1; i < MX; i++) {
c[i][0] = 1;
for (let j = 1; j <= i; j++) {
c[i][j] = (c[i - 1][j] + c[i - 1][j - 1]) % MOD;
}
}
})();

function countBalancedPermutations(num: string): number {
const cnt = Array(10).fill(0);
let s = 0;
for (const ch of num) {
cnt[+ch]++;
s += +ch;
}

if (s % 2 !== 0) {
return 0;
}

const n = num.length;
const m = Math.floor(n / 2) + 1;
const f: Record<string, number> = {};

const dfs = (i: number, j: number, a: number, b: number): number => {
if (i > 9) {
return (j | a | b) === 0 ? 1 : 0;
}
if (a === 0 && j > 0) {
return 0;
}

const key = `${i},${j},${a},${b}`;
if (key in f) {
return f[key];
}

let ans = 0;
for (let l = 0; l <= Math.min(cnt[i], a); l++) {
const r = cnt[i] - l;
if (r >= 0 && r <= b && l * i <= j) {
const t = Number(
(((BigInt(c[a][l]) * BigInt(c[b][r])) % BigInt(MOD)) *
BigInt(dfs(i + 1, j - l * i, a - l, b - r))) %
BigInt(MOD),
);
ans = (ans + t) % MOD;
}
}
f[key] = ans;
return ans;
};

return dfs(0, s / 2, Math.floor(n / 2), Math.floor((n + 1) / 2));
}
```

<!-- tabs:end -->
Expand Down
Loading

0 comments on commit fa38c56

Please sign in to comment.