build
This commit is contained in:
parent
ca49da47f0
commit
5f4a7728b2
@ -1124,7 +1124,7 @@ comments: true
|
||||
|
||||
如图 4-8 所示,常见的链表类型包括三种。
|
||||
|
||||
- **单向链表**:即上述介绍的普通链表。单向链表的节点包含值和指向下一节点的引用两项数据。我们将首个节点称为头节点,将最后一个节点成为尾节点,尾节点指向空 $\text{None}$ 。
|
||||
- **单向链表**:即上述介绍的普通链表。单向链表的节点包含值和指向下一节点的引用两项数据。我们将首个节点称为头节点,将最后一个节点称为尾节点,尾节点指向空 $\text{None}$ 。
|
||||
- **环形链表**:如果我们令单向链表的尾节点指向头节点(即首尾相接),则得到一个环形链表。在环形链表中,任意节点都可以视作头节点。
|
||||
- **双向链表**:与单向链表相比,双向链表记录了两个方向的引用。双向链表的节点定义同时包含指向后继节点(下一个节点)和前驱节点(上一个节点)的引用(指针)。相较于单向链表,双向链表更具灵活性,可以朝两个方向遍历链表,但相应地也需要占用更多的内存空间。
|
||||
|
||||
|
||||
@ -29,8 +29,8 @@ comments: true
|
||||
$$
|
||||
\begin{aligned}
|
||||
& 1 + (-2) \newline
|
||||
& \rightarrow 0000 \space 0001 + 1000 \space 0010 \newline
|
||||
& = 1000 \space 0011 \newline
|
||||
& \rightarrow 0000 \; 0001 + 1000 \; 0010 \newline
|
||||
& = 1000 \; 0011 \newline
|
||||
& \rightarrow -3
|
||||
\end{aligned}
|
||||
$$
|
||||
@ -40,10 +40,10 @@ $$
|
||||
$$
|
||||
\begin{aligned}
|
||||
& 1 + (-2) \newline
|
||||
& \rightarrow 0000 \space 0001 \space \text{(原码)} + 1000 \space 0010 \space \text{(原码)} \newline
|
||||
& = 0000 \space 0001 \space \text{(反码)} + 1111 \space 1101 \space \text{(反码)} \newline
|
||||
& = 1111 \space 1110 \space \text{(反码)} \newline
|
||||
& = 1000 \space 0001 \space \text{(原码)} \newline
|
||||
& \rightarrow 0000 \; 0001 \; \text{(原码)} + 1000 \; 0010 \; \text{(原码)} \newline
|
||||
& = 0000 \; 0001 \; \text{(反码)} + 1111 \; 1101 \; \text{(反码)} \newline
|
||||
& = 1111 \; 1110 \; \text{(反码)} \newline
|
||||
& = 1000 \; 0001 \; \text{(原码)} \newline
|
||||
& \rightarrow -1
|
||||
\end{aligned}
|
||||
$$
|
||||
@ -52,8 +52,8 @@ $$
|
||||
|
||||
$$
|
||||
\begin{aligned}
|
||||
+0 & \rightarrow 0000 \space 0000 \newline
|
||||
-0 & \rightarrow 1000 \space 0000
|
||||
+0 & \rightarrow 0000 \; 0000 \newline
|
||||
-0 & \rightarrow 1000 \; 0000
|
||||
\end{aligned}
|
||||
$$
|
||||
|
||||
@ -61,25 +61,25 @@ $$
|
||||
|
||||
$$
|
||||
\begin{aligned}
|
||||
-0 \rightarrow \space & 1000 \space 0000 \space \text{(原码)} \newline
|
||||
= \space & 1111 \space 1111 \space \text{(反码)} \newline
|
||||
= 1 \space & 0000 \space 0000 \space \text{(补码)} \newline
|
||||
-0 \rightarrow \; & 1000 \; 0000 \; \text{(原码)} \newline
|
||||
= \; & 1111 \; 1111 \; \text{(反码)} \newline
|
||||
= 1 \; & 0000 \; 0000 \; \text{(补码)} \newline
|
||||
\end{aligned}
|
||||
$$
|
||||
|
||||
在负零的反码基础上加 $1$ 会产生进位,但 `byte` 类型的长度只有 8 位,因此溢出到第 9 位的 $1$ 会被舍弃。也就是说,**负零的补码为 $0000 \space 0000$ ,与正零的补码相同**。这意味着在补码表示中只存在一个零,正负零歧义从而得到解决。
|
||||
在负零的反码基础上加 $1$ 会产生进位,但 `byte` 类型的长度只有 8 位,因此溢出到第 9 位的 $1$ 会被舍弃。也就是说,**负零的补码为 $0000 \; 0000$ ,与正零的补码相同**。这意味着在补码表示中只存在一个零,正负零歧义从而得到解决。
|
||||
|
||||
还剩余最后一个疑惑:`byte` 类型的取值范围是 $[-128, 127]$ ,多出来的一个负数 $-128$ 是如何得到的呢?我们注意到,区间 $[-127, +127]$ 内的所有整数都有对应的原码、反码和补码,并且原码和补码之间是可以互相转换的。
|
||||
|
||||
然而,**补码 $1000 \space 0000$ 是一个例外,它并没有对应的原码**。根据转换方法,我们得到该补码的原码为 $0000 \space 0000$ 。这显然是矛盾的,因为该原码表示数字 $0$ ,它的补码应该是自身。计算机规定这个特殊的补码 $1000 \space 0000$ 代表 $-128$ 。实际上,$(-1) + (-127)$ 在补码下的计算结果就是 $-128$ 。
|
||||
然而,**补码 $1000 \; 0000$ 是一个例外,它并没有对应的原码**。根据转换方法,我们得到该补码的原码为 $0000 \; 0000$ 。这显然是矛盾的,因为该原码表示数字 $0$ ,它的补码应该是自身。计算机规定这个特殊的补码 $1000 \; 0000$ 代表 $-128$ 。实际上,$(-1) + (-127)$ 在补码下的计算结果就是 $-128$ 。
|
||||
|
||||
$$
|
||||
\begin{aligned}
|
||||
& (-127) + (-1) \newline
|
||||
& \rightarrow 1111 \space 1111 \space \text{(原码)} + 1000 \space 0001 \space \text{(原码)} \newline
|
||||
& = 1000 \space 0000 \space \text{(反码)} + 1111 \space 1110 \space \text{(反码)} \newline
|
||||
& = 1000 \space 0001 \space \text{(补码)} + 1111 \space 1111 \space \text{(补码)} \newline
|
||||
& = 1000 \space 0000 \space \text{(补码)} \newline
|
||||
& \rightarrow 1111 \; 1111 \; \text{(原码)} + 1000 \; 0001 \; \text{(原码)} \newline
|
||||
& = 1000 \; 0000 \; \text{(反码)} + 1111 \; 1110 \; \text{(反码)} \newline
|
||||
& = 1000 \; 0001 \; \text{(补码)} + 1111 \; 1111 \; \text{(补码)} \newline
|
||||
& = 1000 \; 0000 \; \text{(补码)} \newline
|
||||
& \rightarrow -128
|
||||
\end{aligned}
|
||||
$$
|
||||
|
||||
@ -126,13 +126,45 @@ $$
|
||||
=== "JS"
|
||||
|
||||
```javascript title="min_cost_climbing_stairs_dp.js"
|
||||
[class]{}-[func]{minCostClimbingStairsDP}
|
||||
/* 爬楼梯最小代价:动态规划 */
|
||||
function minCostClimbingStairsDP(cost) {
|
||||
const n = cost.length - 1;
|
||||
if (n === 1 || n === 2) {
|
||||
return cost[n];
|
||||
}
|
||||
// 初始化 dp 表,用于存储子问题的解
|
||||
const dp = new Array(n + 1);
|
||||
// 初始状态:预设最小子问题的解
|
||||
dp[1] = cost[1];
|
||||
dp[2] = cost[2];
|
||||
// 状态转移:从较小子问题逐步求解较大子问题
|
||||
for (let i = 3; i <= n; i++) {
|
||||
dp[i] = Math.min(dp[i - 1], dp[i - 2]) + cost[i];
|
||||
}
|
||||
return dp[n];
|
||||
}
|
||||
```
|
||||
|
||||
=== "TS"
|
||||
|
||||
```typescript title="min_cost_climbing_stairs_dp.ts"
|
||||
[class]{}-[func]{minCostClimbingStairsDP}
|
||||
/* 爬楼梯最小代价:动态规划 */
|
||||
function minCostClimbingStairsDP(cost: Array<number>): number {
|
||||
const n = cost.length - 1;
|
||||
if (n === 1 || n === 2) {
|
||||
return cost[n];
|
||||
}
|
||||
// 初始化 dp 表,用于存储子问题的解
|
||||
const dp = new Array(n + 1);
|
||||
// 初始状态:预设最小子问题的解
|
||||
dp[1] = cost[1];
|
||||
dp[2] = cost[2];
|
||||
// 状态转移:从较小子问题逐步求解较大子问题
|
||||
for (let i = 3; i <= n; i++) {
|
||||
dp[i] = Math.min(dp[i - 1], dp[i - 2]) + cost[i];
|
||||
}
|
||||
return dp[n];
|
||||
}
|
||||
```
|
||||
|
||||
=== "C"
|
||||
@ -328,13 +360,41 @@ $$
|
||||
=== "JS"
|
||||
|
||||
```javascript title="min_cost_climbing_stairs_dp.js"
|
||||
[class]{}-[func]{minCostClimbingStairsDPComp}
|
||||
/* 爬楼梯最小代价:状态压缩后的动态规划 */
|
||||
function minCostClimbingStairsDPComp(cost) {
|
||||
const n = cost.length - 1;
|
||||
if (n === 1 || n === 2) {
|
||||
return cost[n];
|
||||
}
|
||||
let a = cost[1],
|
||||
b = cost[2];
|
||||
for (let i = 3; i <= n; i++) {
|
||||
const tmp = b;
|
||||
b = Math.min(a, tmp) + cost[i];
|
||||
a = tmp;
|
||||
}
|
||||
return b;
|
||||
}
|
||||
```
|
||||
|
||||
=== "TS"
|
||||
|
||||
```typescript title="min_cost_climbing_stairs_dp.ts"
|
||||
[class]{}-[func]{minCostClimbingStairsDPComp}
|
||||
/* 爬楼梯最小代价:状态压缩后的动态规划 */
|
||||
function minCostClimbingStairsDPComp(cost: Array<number>): number {
|
||||
const n = cost.length - 1;
|
||||
if (n === 1 || n === 2) {
|
||||
return cost[n];
|
||||
}
|
||||
let a = cost[1],
|
||||
b = cost[2];
|
||||
for (let i = 3; i <= n; i++) {
|
||||
const tmp = b;
|
||||
b = Math.min(a, tmp) + cost[i];
|
||||
a = tmp;
|
||||
}
|
||||
return b;
|
||||
}
|
||||
```
|
||||
|
||||
=== "C"
|
||||
@ -569,13 +629,52 @@ $$
|
||||
=== "JS"
|
||||
|
||||
```javascript title="climbing_stairs_constraint_dp.js"
|
||||
[class]{}-[func]{climbingStairsConstraintDP}
|
||||
/* 带约束爬楼梯:动态规划 */
|
||||
function climbingStairsConstraintDP(n) {
|
||||
if (n === 1 || n === 2) {
|
||||
return n;
|
||||
}
|
||||
// 初始化 dp 表,用于存储子问题的解
|
||||
const dp = Array.from(new Array(n + 1), () => new Array(3));
|
||||
// 初始状态:预设最小子问题的解
|
||||
dp[1][1] = 1;
|
||||
dp[1][2] = 0;
|
||||
dp[2][1] = 0;
|
||||
dp[2][2] = 1;
|
||||
// 状态转移:从较小子问题逐步求解较大子问题
|
||||
for (let i = 3; i <= n; i++) {
|
||||
dp[i][1] = dp[i - 1][2];
|
||||
dp[i][2] = dp[i - 2][1] + dp[i - 2][2];
|
||||
}
|
||||
return dp[n][1] + dp[n][2];
|
||||
}
|
||||
```
|
||||
|
||||
=== "TS"
|
||||
|
||||
```typescript title="climbing_stairs_constraint_dp.ts"
|
||||
[class]{}-[func]{climbingStairsConstraintDP}
|
||||
/* 带约束爬楼梯:动态规划 */
|
||||
function climbingStairsConstraintDP(n: number): number {
|
||||
if (n === 1 || n === 2) {
|
||||
return n;
|
||||
}
|
||||
// 初始化 dp 表,用于存储子问题的解
|
||||
const dp = Array.from(
|
||||
{ length: n + 1 },
|
||||
() => new Array(3)
|
||||
);
|
||||
// 初始状态:预设最小子问题的解
|
||||
dp[1][1] = 1;
|
||||
dp[1][2] = 0;
|
||||
dp[2][1] = 0;
|
||||
dp[2][2] = 1;
|
||||
// 状态转移:从较小子问题逐步求解较大子问题
|
||||
for (let i = 3; i <= n; i++) {
|
||||
dp[i][1] = dp[i - 1][2];
|
||||
dp[i][2] = dp[i - 2][1] + dp[i - 2][2];
|
||||
}
|
||||
return dp[n][1] + dp[n][2];
|
||||
}
|
||||
```
|
||||
|
||||
=== "C"
|
||||
|
||||
@ -195,13 +195,47 @@ $$
|
||||
=== "JS"
|
||||
|
||||
```javascript title="min_path_sum.js"
|
||||
[class]{}-[func]{minPathSumDFS}
|
||||
/* 最小路径和:暴力搜索 */
|
||||
function minPathSumDFS(grid, i, j) {
|
||||
// 若为左上角单元格,则终止搜索
|
||||
if (i === 0 && j === 0) {
|
||||
return grid[0][0];
|
||||
}
|
||||
// 若行列索引越界,则返回 +∞ 代价
|
||||
if (i < 0 || j < 0) {
|
||||
return Infinity;
|
||||
}
|
||||
// 计算从左上角到 (i-1, j) 和 (i, j-1) 的最小路径代价
|
||||
const left = minPathSumDFS(grid, i - 1, j);
|
||||
const up = minPathSumDFS(grid, i, j - 1);
|
||||
// 返回从左上角到 (i, j) 的最小路径代价
|
||||
return Math.min(left, up) + grid[i][j];
|
||||
}
|
||||
```
|
||||
|
||||
=== "TS"
|
||||
|
||||
```typescript title="min_path_sum.ts"
|
||||
[class]{}-[func]{minPathSumDFS}
|
||||
/* 最小路径和:暴力搜索 */
|
||||
function minPathSumDFS(
|
||||
grid: Array<Array<number>>,
|
||||
i: number,
|
||||
j: number
|
||||
): number {
|
||||
// 若为左上角单元格,则终止搜索
|
||||
if (i === 0 && j == 0) {
|
||||
return grid[0][0];
|
||||
}
|
||||
// 若行列索引越界,则返回 +∞ 代价
|
||||
if (i < 0 || j < 0) {
|
||||
return Infinity;
|
||||
}
|
||||
// 计算从左上角到 (i-1, j) 和 (i, j-1) 的最小路径代价
|
||||
const left = minPathSumDFS(grid, i - 1, j);
|
||||
const up = minPathSumDFS(grid, i, j - 1);
|
||||
// 返回从左上角到 (i, j) 的最小路径代价
|
||||
return Math.min(left, up) + grid[i][j];
|
||||
}
|
||||
```
|
||||
|
||||
=== "C"
|
||||
@ -435,13 +469,58 @@ $$
|
||||
=== "JS"
|
||||
|
||||
```javascript title="min_path_sum.js"
|
||||
[class]{}-[func]{minPathSumDFSMem}
|
||||
/* 最小路径和:记忆化搜索 */
|
||||
function minPathSumDFSMem(grid, mem, i, j) {
|
||||
// 若为左上角单元格,则终止搜索
|
||||
if (i === 0 && j === 0) {
|
||||
return grid[0][0];
|
||||
}
|
||||
// 若行列索引越界,则返回 +∞ 代价
|
||||
if (i < 0 || j < 0) {
|
||||
return Infinity;
|
||||
}
|
||||
// 若已有记录,则直接返回
|
||||
if (mem[i][j] !== -1) {
|
||||
return mem[i][j];
|
||||
}
|
||||
// 左边和上边单元格的最小路径代价
|
||||
const left = minPathSumDFSMem(grid, mem, i - 1, j);
|
||||
const up = minPathSumDFSMem(grid, mem, i, j - 1);
|
||||
// 记录并返回左上角到 (i, j) 的最小路径代价
|
||||
mem[i][j] = Math.min(left, up) + grid[i][j];
|
||||
return mem[i][j];
|
||||
}
|
||||
```
|
||||
|
||||
=== "TS"
|
||||
|
||||
```typescript title="min_path_sum.ts"
|
||||
[class]{}-[func]{minPathSumDFSMem}
|
||||
/* 最小路径和:记忆化搜索 */
|
||||
function minPathSumDFSMem(
|
||||
grid: Array<Array<number>>,
|
||||
mem: Array<Array<number>>,
|
||||
i: number,
|
||||
j: number
|
||||
): number {
|
||||
// 若为左上角单元格,则终止搜索
|
||||
if (i === 0 && j === 0) {
|
||||
return grid[0][0];
|
||||
}
|
||||
// 若行列索引越界,则返回 +∞ 代价
|
||||
if (i < 0 || j < 0) {
|
||||
return Infinity;
|
||||
}
|
||||
// 若已有记录,则直接返回
|
||||
if (mem[i][j] != -1) {
|
||||
return mem[i][j];
|
||||
}
|
||||
// 左边和上边单元格的最小路径代价
|
||||
const left = minPathSumDFSMem(grid, mem, i - 1, j);
|
||||
const up = minPathSumDFSMem(grid, mem, i, j - 1);
|
||||
// 记录并返回左上角到 (i, j) 的最小路径代价
|
||||
mem[i][j] = Math.min(left, up) + grid[i][j];
|
||||
return mem[i][j];
|
||||
}
|
||||
```
|
||||
|
||||
=== "C"
|
||||
@ -701,13 +780,61 @@ $$
|
||||
=== "JS"
|
||||
|
||||
```javascript title="min_path_sum.js"
|
||||
[class]{}-[func]{minPathSumDP}
|
||||
/* 最小路径和:动态规划 */
|
||||
function minPathSumDP(grid) {
|
||||
const n = grid.length,
|
||||
m = grid[0].length;
|
||||
// 初始化 dp 表
|
||||
const dp = Array.from({ length: n }, () =>
|
||||
Array.from({ length: m }, () => 0)
|
||||
);
|
||||
dp[0][0] = grid[0][0];
|
||||
// 状态转移:首行
|
||||
for (let j = 1; j < m; j++) {
|
||||
dp[0][j] = dp[0][j - 1] + grid[0][j];
|
||||
}
|
||||
// 状态转移:首列
|
||||
for (let i = 1; i < n; i++) {
|
||||
dp[i][0] = dp[i - 1][0] + grid[i][0];
|
||||
}
|
||||
// 状态转移:其余行列
|
||||
for (let i = 1; i < n; i++) {
|
||||
for (let j = 1; j < m; j++) {
|
||||
dp[i][j] = Math.min(dp[i][j - 1], dp[i - 1][j]) + grid[i][j];
|
||||
}
|
||||
}
|
||||
return dp[n - 1][m - 1];
|
||||
}
|
||||
```
|
||||
|
||||
=== "TS"
|
||||
|
||||
```typescript title="min_path_sum.ts"
|
||||
[class]{}-[func]{minPathSumDP}
|
||||
/* 最小路径和:动态规划 */
|
||||
function minPathSumDP(grid: Array<Array<number>>): number {
|
||||
const n = grid.length,
|
||||
m = grid[0].length;
|
||||
// 初始化 dp 表
|
||||
const dp = Array.from({ length: n }, () =>
|
||||
Array.from({ length: m }, () => 0)
|
||||
);
|
||||
dp[0][0] = grid[0][0];
|
||||
// 状态转移:首行
|
||||
for (let j = 1; j < m; j++) {
|
||||
dp[0][j] = dp[0][j - 1] + grid[0][j];
|
||||
}
|
||||
// 状态转移:首列
|
||||
for (let i = 1; i < n; i++) {
|
||||
dp[i][0] = dp[i - 1][0] + grid[i][0];
|
||||
}
|
||||
// 状态转移:其余行列
|
||||
for (let i = 1; i < n; i++) {
|
||||
for (let j: number = 1; j < m; j++) {
|
||||
dp[i][j] = Math.min(dp[i][j - 1], dp[i - 1][j]) + grid[i][j];
|
||||
}
|
||||
}
|
||||
return dp[n - 1][m - 1];
|
||||
}
|
||||
```
|
||||
|
||||
=== "C"
|
||||
@ -1004,13 +1131,55 @@ $$
|
||||
=== "JS"
|
||||
|
||||
```javascript title="min_path_sum.js"
|
||||
[class]{}-[func]{minPathSumDPComp}
|
||||
/* 最小路径和:状态压缩后的动态规划 */
|
||||
function minPathSumDPComp(grid) {
|
||||
const n = grid.length,
|
||||
m = grid[0].length;
|
||||
// 初始化 dp 表
|
||||
const dp = new Array(m);
|
||||
// 状态转移:首行
|
||||
dp[0] = grid[0][0];
|
||||
for (let j = 1; j < m; j++) {
|
||||
dp[j] = dp[j - 1] + grid[0][j];
|
||||
}
|
||||
// 状态转移:其余行
|
||||
for (let i = 1; i < n; i++) {
|
||||
// 状态转移:首列
|
||||
dp[0] = dp[0] + grid[i][0];
|
||||
// 状态转移:其余列
|
||||
for (let j = 1; j < m; j++) {
|
||||
dp[j] = Math.min(dp[j - 1], dp[j]) + grid[i][j];
|
||||
}
|
||||
}
|
||||
return dp[m - 1];
|
||||
}
|
||||
```
|
||||
|
||||
=== "TS"
|
||||
|
||||
```typescript title="min_path_sum.ts"
|
||||
[class]{}-[func]{minPathSumDPComp}
|
||||
/* 最小路径和:状态压缩后的动态规划 */
|
||||
function minPathSumDPComp(grid: Array<Array<number>>): number {
|
||||
const n = grid.length,
|
||||
m = grid[0].length;
|
||||
// 初始化 dp 表
|
||||
const dp = new Array(m);
|
||||
// 状态转移:首行
|
||||
dp[0] = grid[0][0];
|
||||
for (let j = 1; j < m; j++) {
|
||||
dp[j] = dp[j - 1] + grid[0][j];
|
||||
}
|
||||
// 状态转移:其余行
|
||||
for (let i = 1; i < n; i++) {
|
||||
// 状态转移:首列
|
||||
dp[0] = dp[0] + grid[i][0];
|
||||
// 状态转移:其余列
|
||||
for (let j = 1; j < m; j++) {
|
||||
dp[j] = Math.min(dp[j - 1], dp[j]) + grid[i][j];
|
||||
}
|
||||
}
|
||||
return dp[m - 1];
|
||||
}
|
||||
```
|
||||
|
||||
=== "C"
|
||||
|
||||
@ -199,13 +199,73 @@ $$
|
||||
=== "JS"
|
||||
|
||||
```javascript title="edit_distance.js"
|
||||
[class]{}-[func]{editDistanceDP}
|
||||
/* 编辑距离:动态规划 */
|
||||
function editDistanceDP(s, t) {
|
||||
const n = s.length,
|
||||
m = t.length;
|
||||
const dp = Array.from({ length: n + 1 }, () => new Array(m + 1).fill(0));
|
||||
// 状态转移:首行首列
|
||||
for (let i = 1; i <= n; i++) {
|
||||
dp[i][0] = i;
|
||||
}
|
||||
for (let j = 1; j <= m; j++) {
|
||||
dp[0][j] = j;
|
||||
}
|
||||
// 状态转移:其余行列
|
||||
for (let i = 1; i <= n; i++) {
|
||||
for (let j = 1; j <= m; j++) {
|
||||
if (s.charAt(i - 1) === t.charAt(j - 1)) {
|
||||
// 若两字符相等,则直接跳过此两字符
|
||||
dp[i][j] = dp[i - 1][j - 1];
|
||||
} else {
|
||||
// 最少编辑步数 = 插入、删除、替换这三种操作的最少编辑步数 + 1
|
||||
dp[i][j] =
|
||||
Math.min(
|
||||
Math.min(dp[i][j - 1], dp[i - 1][j]),
|
||||
dp[i - 1][j - 1]
|
||||
) + 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
return dp[n][m];
|
||||
}
|
||||
```
|
||||
|
||||
=== "TS"
|
||||
|
||||
```typescript title="edit_distance.ts"
|
||||
[class]{}-[func]{editDistanceDP}
|
||||
/* 编辑距离:动态规划 */
|
||||
function editDistanceDP(s: string, t: string): number {
|
||||
const n = s.length,
|
||||
m = t.length;
|
||||
const dp = Array.from({ length: n + 1 }, () =>
|
||||
Array.from({ length: m + 1 }, () => 0)
|
||||
);
|
||||
// 状态转移:首行首列
|
||||
for (let i = 1; i <= n; i++) {
|
||||
dp[i][0] = i;
|
||||
}
|
||||
for (let j = 1; j <= m; j++) {
|
||||
dp[0][j] = j;
|
||||
}
|
||||
// 状态转移:其余行列
|
||||
for (let i = 1; i <= n; i++) {
|
||||
for (let j = 1; j <= m; j++) {
|
||||
if (s.charAt(i - 1) === t.charAt(j - 1)) {
|
||||
// 若两字符相等,则直接跳过此两字符
|
||||
dp[i][j] = dp[i - 1][j - 1];
|
||||
} else {
|
||||
// 最少编辑步数 = 插入、删除、替换这三种操作的最少编辑步数 + 1
|
||||
dp[i][j] =
|
||||
Math.min(
|
||||
Math.min(dp[i][j - 1], dp[i - 1][j]),
|
||||
dp[i - 1][j - 1]
|
||||
) + 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
return dp[n][m];
|
||||
}
|
||||
```
|
||||
|
||||
=== "C"
|
||||
@ -552,13 +612,69 @@ $$
|
||||
=== "JS"
|
||||
|
||||
```javascript title="edit_distance.js"
|
||||
[class]{}-[func]{editDistanceDPComp}
|
||||
/* 编辑距离:状态压缩后的动态规划 */
|
||||
function editDistanceDPComp(s, t) {
|
||||
const n = s.length,
|
||||
m = t.length;
|
||||
const dp = new Array(m + 1).fill(0);
|
||||
// 状态转移:首行
|
||||
for (let j = 1; j <= m; j++) {
|
||||
dp[j] = j;
|
||||
}
|
||||
// 状态转移:其余行
|
||||
for (let i = 1; i <= n; i++) {
|
||||
// 状态转移:首列
|
||||
let leftup = dp[0]; // 暂存 dp[i-1, j-1]
|
||||
dp[0] = i;
|
||||
// 状态转移:其余列
|
||||
for (let j = 1; j <= m; j++) {
|
||||
const temp = dp[j];
|
||||
if (s.charAt(i - 1) === t.charAt(j - 1)) {
|
||||
// 若两字符相等,则直接跳过此两字符
|
||||
dp[j] = leftup;
|
||||
} else {
|
||||
// 最少编辑步数 = 插入、删除、替换这三种操作的最少编辑步数 + 1
|
||||
dp[j] = Math.min(Math.min(dp[j - 1], dp[j]), leftup) + 1;
|
||||
}
|
||||
leftup = temp; // 更新为下一轮的 dp[i-1, j-1]
|
||||
}
|
||||
}
|
||||
return dp[m];
|
||||
}
|
||||
```
|
||||
|
||||
=== "TS"
|
||||
|
||||
```typescript title="edit_distance.ts"
|
||||
[class]{}-[func]{editDistanceDPComp}
|
||||
/* 编辑距离:状态压缩后的动态规划 */
|
||||
function editDistanceDPComp(s: string, t: string): number {
|
||||
const n = s.length,
|
||||
m = t.length;
|
||||
const dp = new Array(m + 1).fill(0);
|
||||
// 状态转移:首行
|
||||
for (let j = 1; j <= m; j++) {
|
||||
dp[j] = j;
|
||||
}
|
||||
// 状态转移:其余行
|
||||
for (let i = 1; i <= n; i++) {
|
||||
// 状态转移:首列
|
||||
let leftup = dp[0]; // 暂存 dp[i-1, j-1]
|
||||
dp[0] = i;
|
||||
// 状态转移:其余列
|
||||
for (let j = 1; j <= m; j++) {
|
||||
const temp = dp[j];
|
||||
if (s.charAt(i - 1) === t.charAt(j - 1)) {
|
||||
// 若两字符相等,则直接跳过此两字符
|
||||
dp[j] = leftup;
|
||||
} else {
|
||||
// 最少编辑步数 = 插入、删除、替换这三种操作的最少编辑步数 + 1
|
||||
dp[j] = Math.min(Math.min(dp[j - 1], dp[j]), leftup) + 1;
|
||||
}
|
||||
leftup = temp; // 更新为下一轮的 dp[i-1, j-1]
|
||||
}
|
||||
}
|
||||
return dp[m];
|
||||
}
|
||||
```
|
||||
|
||||
=== "C"
|
||||
|
||||
@ -147,13 +147,49 @@ $$
|
||||
=== "JS"
|
||||
|
||||
```javascript title="knapsack.js"
|
||||
[class]{}-[func]{knapsackDFS}
|
||||
/* 0-1 背包:暴力搜索 */
|
||||
function knapsackDFS(wgt, val, i, c) {
|
||||
// 若已选完所有物品或背包无容量,则返回价值 0
|
||||
if (i === 0 || c === 0) {
|
||||
return 0;
|
||||
}
|
||||
// 若超过背包容量,则只能不放入背包
|
||||
if (wgt[i - 1] > c) {
|
||||
return knapsackDFS(wgt, val, i - 1, c);
|
||||
}
|
||||
// 计算不放入和放入物品 i 的最大价值
|
||||
const no = knapsackDFS(wgt, val, i - 1, c);
|
||||
const yes = knapsackDFS(wgt, val, i - 1, c - wgt[i - 1]) + val[i - 1];
|
||||
// 返回两种方案中价值更大的那一个
|
||||
return Math.max(no, yes);
|
||||
}
|
||||
```
|
||||
|
||||
=== "TS"
|
||||
|
||||
```typescript title="knapsack.ts"
|
||||
[class]{}-[func]{knapsackDFS}
|
||||
/* 0-1 背包:暴力搜索 */
|
||||
function knapsackDFS(
|
||||
wgt: Array<number>,
|
||||
val: Array<number>,
|
||||
i: number,
|
||||
c: number
|
||||
): number {
|
||||
// 若已选完所有物品或背包无容量,则返回价值 0
|
||||
if (i === 0 || c === 0) {
|
||||
return 0;
|
||||
}
|
||||
// 若超过背包容量,则只能不放入背包
|
||||
if (wgt[i - 1] > c) {
|
||||
return knapsackDFS(wgt, val, i - 1, c);
|
||||
}
|
||||
// 计算不放入和放入物品 i 的最大价值
|
||||
const no = knapsackDFS(wgt, val, i - 1, c);
|
||||
const yes =
|
||||
knapsackDFS(wgt, val, i - 1, c - wgt[i - 1]) + val[i - 1];
|
||||
// 返回两种方案中价值更大的那一个
|
||||
return Math.max(no, yes);
|
||||
}
|
||||
```
|
||||
|
||||
=== "C"
|
||||
@ -386,13 +422,60 @@ $$
|
||||
=== "JS"
|
||||
|
||||
```javascript title="knapsack.js"
|
||||
[class]{}-[func]{knapsackDFSMem}
|
||||
/* 0-1 背包:记忆化搜索 */
|
||||
function knapsackDFSMem(wgt, val, mem, i, c) {
|
||||
// 若已选完所有物品或背包无容量,则返回价值 0
|
||||
if (i === 0 || c === 0) {
|
||||
return 0;
|
||||
}
|
||||
// 若已有记录,则直接返回
|
||||
if (mem[i][c] !== -1) {
|
||||
return mem[i][c];
|
||||
}
|
||||
// 若超过背包容量,则只能不放入背包
|
||||
if (wgt[i - 1] > c) {
|
||||
return knapsackDFSMem(wgt, val, mem, i - 1, c);
|
||||
}
|
||||
// 计算不放入和放入物品 i 的最大价值
|
||||
const no = knapsackDFSMem(wgt, val, mem, i - 1, c);
|
||||
const yes = knapsackDFSMem(wgt, val, mem, i - 1, c - wgt[i - 1]) + val[i - 1];
|
||||
// 记录并返回两种方案中价值更大的那一个
|
||||
mem[i][c] = Math.max(no, yes);
|
||||
return mem[i][c];
|
||||
}
|
||||
```
|
||||
|
||||
=== "TS"
|
||||
|
||||
```typescript title="knapsack.ts"
|
||||
[class]{}-[func]{knapsackDFSMem}
|
||||
/* 0-1 背包:记忆化搜索 */
|
||||
function knapsackDFSMem(
|
||||
wgt: Array<number>,
|
||||
val: Array<number>,
|
||||
mem: Array<Array<number>>,
|
||||
i: number,
|
||||
c: number
|
||||
): number {
|
||||
// 若已选完所有物品或背包无容量,则返回价值 0
|
||||
if (i === 0 || c === 0) {
|
||||
return 0;
|
||||
}
|
||||
// 若已有记录,则直接返回
|
||||
if (mem[i][c] !== -1) {
|
||||
return mem[i][c];
|
||||
}
|
||||
// 若超过背包容量,则只能不放入背包
|
||||
if (wgt[i - 1] > c) {
|
||||
return knapsackDFSMem(wgt, val, mem, i - 1, c);
|
||||
}
|
||||
// 计算不放入和放入物品 i 的最大价值
|
||||
const no = knapsackDFSMem(wgt, val, mem, i - 1, c);
|
||||
const yes =
|
||||
knapsackDFSMem(wgt, val, mem, i - 1, c - wgt[i - 1]) + val[i - 1];
|
||||
// 记录并返回两种方案中价值更大的那一个
|
||||
mem[i][c] = Math.max(no, yes);
|
||||
return mem[i][c];
|
||||
}
|
||||
```
|
||||
|
||||
=== "C"
|
||||
@ -645,13 +728,63 @@ $$
|
||||
=== "JS"
|
||||
|
||||
```javascript title="knapsack.js"
|
||||
[class]{}-[func]{knapsackDP}
|
||||
/* 0-1 背包:动态规划 */
|
||||
function knapsackDP(wgt, val, cap) {
|
||||
const n = wgt.length;
|
||||
// 初始化 dp 表
|
||||
const dp = Array(n + 1)
|
||||
.fill(0)
|
||||
.map(() => Array(cap + 1).fill(0));
|
||||
// 状态转移
|
||||
for (let i = 1; i <= n; i++) {
|
||||
for (let c = 1; c <= cap; c++) {
|
||||
if (wgt[i - 1] > c) {
|
||||
// 若超过背包容量,则不选物品 i
|
||||
dp[i][c] = dp[i - 1][c];
|
||||
} else {
|
||||
// 不选和选物品 i 这两种方案的较大值
|
||||
dp[i][c] = Math.max(
|
||||
dp[i - 1][c],
|
||||
dp[i - 1][c - wgt[i - 1]] + val[i - 1]
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
return dp[n][cap];
|
||||
}
|
||||
```
|
||||
|
||||
=== "TS"
|
||||
|
||||
```typescript title="knapsack.ts"
|
||||
[class]{}-[func]{knapsackDP}
|
||||
/* 0-1 背包:动态规划 */
|
||||
function knapsackDP(
|
||||
wgt: Array<number>,
|
||||
val: Array<number>,
|
||||
cap: number
|
||||
): number {
|
||||
const n = wgt.length;
|
||||
// 初始化 dp 表
|
||||
const dp = Array.from({ length: n + 1 }, () =>
|
||||
Array.from({ length: cap + 1 }, () => 0)
|
||||
);
|
||||
// 状态转移
|
||||
for (let i = 1; i <= n; i++) {
|
||||
for (let c = 1; c <= cap; c++) {
|
||||
if (wgt[i - 1] > c) {
|
||||
// 若超过背包容量,则不选物品 i
|
||||
dp[i][c] = dp[i - 1][c];
|
||||
} else {
|
||||
// 不选和选物品 i 这两种方案的较大值
|
||||
dp[i][c] = Math.max(
|
||||
dp[i - 1][c],
|
||||
dp[i - 1][c - wgt[i - 1]] + val[i - 1]
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
return dp[n][cap];
|
||||
}
|
||||
```
|
||||
|
||||
=== "C"
|
||||
@ -949,13 +1082,49 @@ $$
|
||||
=== "JS"
|
||||
|
||||
```javascript title="knapsack.js"
|
||||
[class]{}-[func]{knapsackDPComp}
|
||||
/* 0-1 背包:状态压缩后的动态规划 */
|
||||
function knapsackDPComp(wgt, val, cap) {
|
||||
const n = wgt.length;
|
||||
// 初始化 dp 表
|
||||
const dp = Array(cap + 1).fill(0);
|
||||
// 状态转移
|
||||
for (let i = 1; i <= n; i++) {
|
||||
// 倒序遍历
|
||||
for (let c = cap; c >= 1; c--) {
|
||||
if (wgt[i - 1] <= c) {
|
||||
// 不选和选物品 i 这两种方案的较大值
|
||||
dp[c] = Math.max(dp[c], dp[c - wgt[i - 1]] + val[i - 1]);
|
||||
}
|
||||
}
|
||||
}
|
||||
return dp[cap];
|
||||
}
|
||||
```
|
||||
|
||||
=== "TS"
|
||||
|
||||
```typescript title="knapsack.ts"
|
||||
[class]{}-[func]{knapsackDPComp}
|
||||
/* 0-1 背包:状态压缩后的动态规划 */
|
||||
function knapsackDPComp(
|
||||
wgt: Array<number>,
|
||||
val: Array<number>,
|
||||
cap: number
|
||||
): number {
|
||||
const n = wgt.length;
|
||||
// 初始化 dp 表
|
||||
const dp = Array(cap + 1).fill(0);
|
||||
// 状态转移
|
||||
for (let i = 1; i <= n; i++) {
|
||||
// 倒序遍历
|
||||
for (let c = cap; c >= 1; c--) {
|
||||
if (wgt[i - 1] <= c) {
|
||||
// 不选和选物品 i 这两种方案的较大值
|
||||
dp[c] = Math.max(dp[c], dp[c - wgt[i - 1]] + val[i - 1]);
|
||||
}
|
||||
}
|
||||
}
|
||||
return dp[cap];
|
||||
}
|
||||
```
|
||||
|
||||
=== "C"
|
||||
|
||||
@ -137,13 +137,63 @@ $$
|
||||
=== "JS"
|
||||
|
||||
```javascript title="unbounded_knapsack.js"
|
||||
[class]{}-[func]{unboundedKnapsackDP}
|
||||
/* 完全背包:动态规划 */
|
||||
function unboundedKnapsackDP(wgt, val, cap) {
|
||||
const n = wgt.length;
|
||||
// 初始化 dp 表
|
||||
const dp = Array.from({ length: n + 1 }, () =>
|
||||
Array.from({ length: cap + 1 }, () => 0)
|
||||
);
|
||||
// 状态转移
|
||||
for (let i = 1; i <= n; i++) {
|
||||
for (let c = 1; c <= cap; c++) {
|
||||
if (wgt[i - 1] > c) {
|
||||
// 若超过背包容量,则不选物品 i
|
||||
dp[i][c] = dp[i - 1][c];
|
||||
} else {
|
||||
// 不选和选物品 i 这两种方案的较大值
|
||||
dp[i][c] = Math.max(
|
||||
dp[i - 1][c],
|
||||
dp[i][c - wgt[i - 1]] + val[i - 1]
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
return dp[n][cap];
|
||||
}
|
||||
```
|
||||
|
||||
=== "TS"
|
||||
|
||||
```typescript title="unbounded_knapsack.ts"
|
||||
[class]{}-[func]{unboundedKnapsackDP}
|
||||
/* 完全背包:动态规划 */
|
||||
function unboundedKnapsackDP(
|
||||
wgt: Array<number>,
|
||||
val: Array<number>,
|
||||
cap: number
|
||||
): number {
|
||||
const n = wgt.length;
|
||||
// 初始化 dp 表
|
||||
const dp = Array.from({ length: n + 1 }, () =>
|
||||
Array.from({ length: cap + 1 }, () => 0)
|
||||
);
|
||||
// 状态转移
|
||||
for (let i = 1; i <= n; i++) {
|
||||
for (let c = 1; c <= cap; c++) {
|
||||
if (wgt[i - 1] > c) {
|
||||
// 若超过背包容量,则不选物品 i
|
||||
dp[i][c] = dp[i - 1][c];
|
||||
} else {
|
||||
// 不选和选物品 i 这两种方案的较大值
|
||||
dp[i][c] = Math.max(
|
||||
dp[i - 1][c],
|
||||
dp[i][c - wgt[i - 1]] + val[i - 1]
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
return dp[n][cap];
|
||||
}
|
||||
```
|
||||
|
||||
=== "C"
|
||||
@ -396,13 +446,53 @@ $$
|
||||
=== "JS"
|
||||
|
||||
```javascript title="unbounded_knapsack.js"
|
||||
[class]{}-[func]{unboundedKnapsackDPComp}
|
||||
/* 完全背包:状态压缩后的动态规划 */
|
||||
function unboundedKnapsackDPComp(wgt, val, cap) {
|
||||
const n = wgt.length;
|
||||
// 初始化 dp 表
|
||||
const dp = Array.from({ length: cap + 1 }, () => 0);
|
||||
// 状态转移
|
||||
for (let i = 1; i <= n; i++) {
|
||||
for (let c = 1; c <= cap; c++) {
|
||||
if (wgt[i - 1] > c) {
|
||||
// 若超过背包容量,则不选物品 i
|
||||
dp[c] = dp[c];
|
||||
} else {
|
||||
// 不选和选物品 i 这两种方案的较大值
|
||||
dp[c] = Math.max(dp[c], dp[c - wgt[i - 1]] + val[i - 1]);
|
||||
}
|
||||
}
|
||||
}
|
||||
return dp[cap];
|
||||
}
|
||||
```
|
||||
|
||||
=== "TS"
|
||||
|
||||
```typescript title="unbounded_knapsack.ts"
|
||||
[class]{}-[func]{unboundedKnapsackDPComp}
|
||||
/* 完全背包:状态压缩后的动态规划 */
|
||||
function unboundedKnapsackDPComp(
|
||||
wgt: Array<number>,
|
||||
val: Array<number>,
|
||||
cap: number
|
||||
): number {
|
||||
const n = wgt.length;
|
||||
// 初始化 dp 表
|
||||
const dp = Array.from({ length: cap + 1 }, () => 0);
|
||||
// 状态转移
|
||||
for (let i = 1; i <= n; i++) {
|
||||
for (let c = 1; c <= cap; c++) {
|
||||
if (wgt[i - 1] > c) {
|
||||
// 若超过背包容量,则不选物品 i
|
||||
dp[c] = dp[c];
|
||||
} else {
|
||||
// 不选和选物品 i 这两种方案的较大值
|
||||
dp[c] = Math.max(dp[c], dp[c - wgt[i - 1]] + val[i - 1]);
|
||||
}
|
||||
}
|
||||
}
|
||||
return dp[cap];
|
||||
}
|
||||
```
|
||||
|
||||
=== "C"
|
||||
@ -702,13 +792,63 @@ $$
|
||||
=== "JS"
|
||||
|
||||
```javascript title="coin_change.js"
|
||||
[class]{}-[func]{coinChangeDP}
|
||||
/* 零钱兑换:动态规划 */
|
||||
function coinChangeDP(coins, amt) {
|
||||
const n = coins.length;
|
||||
const MAX = amt + 1;
|
||||
// 初始化 dp 表
|
||||
const dp = Array.from({ length: n + 1 }, () =>
|
||||
Array.from({ length: amt + 1 }, () => 0)
|
||||
);
|
||||
// 状态转移:首行首列
|
||||
for (let a = 1; a <= amt; a++) {
|
||||
dp[0][a] = MAX;
|
||||
}
|
||||
// 状态转移:其余行列
|
||||
for (let i = 1; i <= n; i++) {
|
||||
for (let a = 1; a <= amt; a++) {
|
||||
if (coins[i - 1] > a) {
|
||||
// 若超过背包容量,则不选硬币 i
|
||||
dp[i][a] = dp[i - 1][a];
|
||||
} else {
|
||||
// 不选和选硬币 i 这两种方案的较小值
|
||||
dp[i][a] = Math.min(dp[i - 1][a], dp[i][a - coins[i - 1]] + 1);
|
||||
}
|
||||
}
|
||||
}
|
||||
return dp[n][amt] !== MAX ? dp[n][amt] : -1;
|
||||
}
|
||||
```
|
||||
|
||||
=== "TS"
|
||||
|
||||
```typescript title="coin_change.ts"
|
||||
[class]{}-[func]{coinChangeDP}
|
||||
/* 零钱兑换:动态规划 */
|
||||
function coinChangeDP(coins: Array<number>, amt: number): number {
|
||||
const n = coins.length;
|
||||
const MAX = amt + 1;
|
||||
// 初始化 dp 表
|
||||
const dp = Array.from({ length: n + 1 }, () =>
|
||||
Array.from({ length: amt + 1 }, () => 0)
|
||||
);
|
||||
// 状态转移:首行首列
|
||||
for (let a = 1; a <= amt; a++) {
|
||||
dp[0][a] = MAX;
|
||||
}
|
||||
// 状态转移:其余行列
|
||||
for (let i = 1; i <= n; i++) {
|
||||
for (let a = 1; a <= amt; a++) {
|
||||
if (coins[i - 1] > a) {
|
||||
// 若超过背包容量,则不选硬币 i
|
||||
dp[i][a] = dp[i - 1][a];
|
||||
} else {
|
||||
// 不选和选硬币 i 这两种方案的较小值
|
||||
dp[i][a] = Math.min(dp[i - 1][a], dp[i][a - coins[i - 1]] + 1);
|
||||
}
|
||||
}
|
||||
}
|
||||
return dp[n][amt] !== MAX ? dp[n][amt] : -1;
|
||||
}
|
||||
```
|
||||
|
||||
=== "C"
|
||||
@ -1030,13 +1170,53 @@ $$
|
||||
=== "JS"
|
||||
|
||||
```javascript title="coin_change.js"
|
||||
[class]{}-[func]{coinChangeDPComp}
|
||||
/* 零钱兑换:状态压缩后的动态规划 */
|
||||
function coinChangeDPComp(coins, amt) {
|
||||
const n = coins.length;
|
||||
const MAX = amt + 1;
|
||||
// 初始化 dp 表
|
||||
const dp = Array.from({ length: amt + 1 }, () => MAX);
|
||||
dp[0] = 0;
|
||||
// 状态转移
|
||||
for (let i = 1; i <= n; i++) {
|
||||
for (let a = 1; a <= amt; a++) {
|
||||
if (coins[i - 1] > a) {
|
||||
// 若超过背包容量,则不选硬币 i
|
||||
dp[a] = dp[a];
|
||||
} else {
|
||||
// 不选和选硬币 i 这两种方案的较小值
|
||||
dp[a] = Math.min(dp[a], dp[a - coins[i - 1]] + 1);
|
||||
}
|
||||
}
|
||||
}
|
||||
return dp[amt] !== MAX ? dp[amt] : -1;
|
||||
}
|
||||
```
|
||||
|
||||
=== "TS"
|
||||
|
||||
```typescript title="coin_change.ts"
|
||||
[class]{}-[func]{coinChangeDPComp}
|
||||
/* 零钱兑换:状态压缩后的动态规划 */
|
||||
function coinChangeDPComp(coins: Array<number>, amt: number): number {
|
||||
const n = coins.length;
|
||||
const MAX = amt + 1;
|
||||
// 初始化 dp 表
|
||||
const dp = Array.from({ length: amt + 1 }, () => MAX);
|
||||
dp[0] = 0;
|
||||
// 状态转移
|
||||
for (let i = 1; i <= n; i++) {
|
||||
for (let a = 1; a <= amt; a++) {
|
||||
if (coins[i - 1] > a) {
|
||||
// 若超过背包容量,则不选硬币 i
|
||||
dp[a] = dp[a];
|
||||
} else {
|
||||
// 不选和选硬币 i 这两种方案的较小值
|
||||
dp[a] = Math.min(dp[a], dp[a - coins[i - 1]] + 1);
|
||||
}
|
||||
}
|
||||
}
|
||||
return dp[amt] !== MAX ? dp[amt] : -1;
|
||||
}
|
||||
```
|
||||
|
||||
=== "C"
|
||||
@ -1319,13 +1499,61 @@ $$
|
||||
=== "JS"
|
||||
|
||||
```javascript title="coin_change_ii.js"
|
||||
[class]{}-[func]{coinChangeIIDP}
|
||||
/* 零钱兑换 II:动态规划 */
|
||||
function coinChangeIIDP(coins, amt) {
|
||||
const n = coins.length;
|
||||
// 初始化 dp 表
|
||||
const dp = Array.from({ length: n + 1 }, () =>
|
||||
Array.from({ length: amt + 1 }, () => 0)
|
||||
);
|
||||
// 初始化首列
|
||||
for (let i = 0; i <= n; i++) {
|
||||
dp[i][0] = 1;
|
||||
}
|
||||
// 状态转移
|
||||
for (let i = 1; i <= n; i++) {
|
||||
for (let a = 1; a <= amt; a++) {
|
||||
if (coins[i - 1] > a) {
|
||||
// 若超过背包容量,则不选硬币 i
|
||||
dp[i][a] = dp[i - 1][a];
|
||||
} else {
|
||||
// 不选和选硬币 i 这两种方案之和
|
||||
dp[i][a] = dp[i - 1][a] + dp[i][a - coins[i - 1]];
|
||||
}
|
||||
}
|
||||
}
|
||||
return dp[n][amt];
|
||||
}
|
||||
```
|
||||
|
||||
=== "TS"
|
||||
|
||||
```typescript title="coin_change_ii.ts"
|
||||
[class]{}-[func]{coinChangeIIDP}
|
||||
/* 零钱兑换 II:动态规划 */
|
||||
function coinChangeIIDP(coins: Array<number>, amt: number): number {
|
||||
const n = coins.length;
|
||||
// 初始化 dp 表
|
||||
const dp = Array.from({ length: n + 1 }, () =>
|
||||
Array.from({ length: amt + 1 }, () => 0)
|
||||
);
|
||||
// 初始化首列
|
||||
for (let i = 0; i <= n; i++) {
|
||||
dp[i][0] = 1;
|
||||
}
|
||||
// 状态转移
|
||||
for (let i = 1; i <= n; i++) {
|
||||
for (let a = 1; a <= amt; a++) {
|
||||
if (coins[i - 1] > a) {
|
||||
// 若超过背包容量,则不选硬币 i
|
||||
dp[i][a] = dp[i - 1][a];
|
||||
} else {
|
||||
// 不选和选硬币 i 这两种方案之和
|
||||
dp[i][a] = dp[i - 1][a] + dp[i][a - coins[i - 1]];
|
||||
}
|
||||
}
|
||||
}
|
||||
return dp[n][amt];
|
||||
}
|
||||
```
|
||||
|
||||
=== "C"
|
||||
@ -1579,13 +1807,51 @@ $$
|
||||
=== "JS"
|
||||
|
||||
```javascript title="coin_change_ii.js"
|
||||
[class]{}-[func]{coinChangeIIDPComp}
|
||||
/* 零钱兑换 II:状态压缩后的动态规划 */
|
||||
function coinChangeIIDPComp(coins, amt) {
|
||||
const n = coins.length;
|
||||
// 初始化 dp 表
|
||||
const dp = Array.from({ length: amt + 1 }, () => 0);
|
||||
dp[0] = 1;
|
||||
// 状态转移
|
||||
for (let i = 1; i <= n; i++) {
|
||||
for (let a = 1; a <= amt; a++) {
|
||||
if (coins[i - 1] > a) {
|
||||
// 若超过背包容量,则不选硬币 i
|
||||
dp[a] = dp[a];
|
||||
} else {
|
||||
// 不选和选硬币 i 这两种方案之和
|
||||
dp[a] = dp[a] + dp[a - coins[i - 1]];
|
||||
}
|
||||
}
|
||||
}
|
||||
return dp[amt];
|
||||
}
|
||||
```
|
||||
|
||||
=== "TS"
|
||||
|
||||
```typescript title="coin_change_ii.ts"
|
||||
[class]{}-[func]{coinChangeIIDPComp}
|
||||
/* 零钱兑换 II:状态压缩后的动态规划 */
|
||||
function coinChangeIIDPComp(coins: Array<number>, amt: number): number {
|
||||
const n = coins.length;
|
||||
// 初始化 dp 表
|
||||
const dp = Array.from({ length: amt + 1 }, () => 0);
|
||||
dp[0] = 1;
|
||||
// 状态转移
|
||||
for (let i = 1; i <= n; i++) {
|
||||
for (let a = 1; a <= amt; a++) {
|
||||
if (coins[i - 1] > a) {
|
||||
// 若超过背包容量,则不选硬币 i
|
||||
dp[a] = dp[a];
|
||||
} else {
|
||||
// 不选和选硬币 i 这两种方案之和
|
||||
dp[a] = dp[a] + dp[a - coins[i - 1]];
|
||||
}
|
||||
}
|
||||
}
|
||||
return dp[amt];
|
||||
}
|
||||
```
|
||||
|
||||
=== "C"
|
||||
|
||||
@ -18,7 +18,7 @@ comments: true
|
||||
|
||||
接下来,循环执行以下两步。
|
||||
|
||||
1. 计算中点索引 $m = \lfloor {(i + j) / 2} \rfloor$ ,其中 $\lfloor \space \rfloor$ 表示向下取整操作。
|
||||
1. 计算中点索引 $m = \lfloor {(i + j) / 2} \rfloor$ ,其中 $\lfloor \: \rfloor$ 表示向下取整操作。
|
||||
2. 判断 `nums[m]` 和 `target` 的大小关系,分为以下三种情况。
|
||||
1. 当 `nums[m] < target` 时,说明 `target` 在区间 $[m + 1, j]$ 中,因此执行 $i = m + 1$ 。
|
||||
2. 当 `nums[m] > target` 时,说明 `target` 在区间 $[i, m - 1]$ 中,因此执行 $j = m - 1$ 。
|
||||
|
||||
@ -26,7 +26,7 @@ $$
|
||||
x_k = \lfloor\frac{x}{d^{k-1}}\rfloor \bmod d
|
||||
$$
|
||||
|
||||
其中 $\lfloor a \rfloor$ 表示对浮点数 $a$ 向下取整,而 $\bmod \space d$ 表示对 $d$ 取余。对于学号数据,$d = 10$ 且 $k \in [1, 8]$ 。
|
||||
其中 $\lfloor a \rfloor$ 表示对浮点数 $a$ 向下取整,而 $\bmod \: d$ 表示对 $d$ 取余。对于学号数据,$d = 10$ 且 $k \in [1, 8]$ 。
|
||||
|
||||
此外,我们需要小幅改动计数排序代码,使之可以根据数字的第 $k$ 位进行排序。
|
||||
|
||||
|
||||
Loading…
Reference in New Issue
Block a user