Add ru version (#1865)

* Add Russian docs site baseline

* Add Russian localized codebase

* Polish Russian code wording

* Update ru code translation.

* Update code translation and chapter covers.

* Fix pythontutor extraction.

* Add README and landing page.

* placeholder of profiles

* Use figures of English version

* Remove chapter paperbook
This commit is contained in:
Yudong Jin
2026-03-28 04:24:07 +08:00
committed by GitHub
parent 2ca570cc33
commit 772183705e
1958 changed files with 108186 additions and 0 deletions
@@ -0,0 +1,41 @@
/**
* File: climbing_stairs_backtrack.cs
* Created Time: 2023-06-30
* Author: hpstory (hpstory1024@163.com)
*/
namespace hello_algo.chapter_dynamic_programming;
public class climbing_stairs_backtrack {
/* Бэктрекинг */
void Backtrack(List<int> choices, int state, int n, List<int> res) {
// Когда подъем достигает n-й ступени, число вариантов увеличивается на 1
if (state == n)
res[0]++;
// Перебор всех вариантов выбора
foreach (int choice in choices) {
// Отсечение: нельзя выходить за n-ю ступень
if (state + choice > n)
continue;
// Попытка: сделать выбор и обновить состояние
Backtrack(choices, state + choice, n, res);
// Откат
}
}
/* Подъем по лестнице: бэктрекинг */
int ClimbingStairsBacktrack(int n) {
List<int> choices = [1, 2]; // Можно подняться на 1 или 2 ступени
int state = 0; // Начать подъем с 0-й ступени
List<int> res = [0]; // Использовать res[0] для хранения числа решений
Backtrack(choices, state, n, res);
return res[0];
}
[Test]
public void Test() {
int n = 9;
int res = ClimbingStairsBacktrack(n);
Console.WriteLine($"Количество способов подняться по лестнице из {n} ступеней = {res}");
}
}
@@ -0,0 +1,36 @@
/**
* File: climbing_stairs_constraint_dp.cs
* Created Time: 2023-07-03
* Author: hpstory (hpstory1024@163.com)
*/
namespace hello_algo.chapter_dynamic_programming;
public class climbing_stairs_constraint_dp {
/* Подъем по лестнице с ограничениями: динамическое программирование */
int ClimbingStairsConstraintDP(int n) {
if (n == 1 || n == 2) {
return 1;
}
// Инициализация таблицы dp для хранения решений подзадач
int[,] dp = new int[n + 1, 3];
// Начальное состояние: заранее задать решения наименьших подзадач
dp[1, 1] = 1;
dp[1, 2] = 0;
dp[2, 1] = 0;
dp[2, 2] = 1;
// Переход состояний: постепенное решение больших подзадач через меньшие
for (int 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];
}
[Test]
public void Test() {
int n = 9;
int res = ClimbingStairsConstraintDP(n);
Console.WriteLine($"Количество способов подняться по лестнице из {n} ступеней = {res}");
}
}
@@ -0,0 +1,31 @@
/**
* File: climbing_stairs_dfs.cs
* Created Time: 2023-06-30
* Author: hpstory (hpstory1024@163.com)
*/
namespace hello_algo.chapter_dynamic_programming;
public class climbing_stairs_dfs {
/* Поиск */
int DFS(int i) {
// dp[1] и dp[2] уже известны, вернуть их
if (i == 1 || i == 2)
return i;
// dp[i] = dp[i-1] + dp[i-2]
int count = DFS(i - 1) + DFS(i - 2);
return count;
}
/* Подъем по лестнице: поиск */
int ClimbingStairsDFS(int n) {
return DFS(n);
}
[Test]
public void Test() {
int n = 9;
int res = ClimbingStairsDFS(n);
Console.WriteLine($"Количество способов подняться по лестнице из {n} ступеней = {res}");
}
}
@@ -0,0 +1,39 @@
/**
* File: climbing_stairs_dfs_mem.cs
* Created Time: 2023-06-30
* Author: hpstory (hpstory1024@163.com)
*/
namespace hello_algo.chapter_dynamic_programming;
public class climbing_stairs_dfs_mem {
/* Поиск с мемоизацией */
int DFS(int i, int[] mem) {
// dp[1] и dp[2] уже известны, вернуть их
if (i == 1 || i == 2)
return i;
// Если запись dp[i] существует, сразу вернуть ее
if (mem[i] != -1)
return mem[i];
// dp[i] = dp[i-1] + dp[i-2]
int count = DFS(i - 1, mem) + DFS(i - 2, mem);
// Сохранить dp[i]
mem[i] = count;
return count;
}
/* Подъем по лестнице: поиск с мемоизацией */
int ClimbingStairsDFSMem(int n) {
// mem[i] хранит число способов подняться на i-ю ступень, -1 означает отсутствие записи
int[] mem = new int[n + 1];
Array.Fill(mem, -1);
return DFS(n, mem);
}
[Test]
public void Test() {
int n = 9;
int res = ClimbingStairsDFSMem(n);
Console.WriteLine($"Количество способов подняться по лестнице из {n} ступеней = {res}");
}
}
@@ -0,0 +1,49 @@
/**
* File: climbing_stairs_dp.cs
* Created Time: 2023-06-30
* Author: hpstory (hpstory1024@163.com)
*/
namespace hello_algo.chapter_dynamic_programming;
public class climbing_stairs_dp {
/* Подъем по лестнице: динамическое программирование */
int ClimbingStairsDP(int n) {
if (n == 1 || n == 2)
return n;
// Инициализация таблицы dp для хранения решений подзадач
int[] dp = new int[n + 1];
// Начальное состояние: заранее задать решения наименьших подзадач
dp[1] = 1;
dp[2] = 2;
// Переход состояний: постепенное решение больших подзадач через меньшие
for (int i = 3; i <= n; i++) {
dp[i] = dp[i - 1] + dp[i - 2];
}
return dp[n];
}
/* Подъем по лестнице: динамическое программирование с оптимизацией памяти */
int ClimbingStairsDPComp(int n) {
if (n == 1 || n == 2)
return n;
int a = 1, b = 2;
for (int i = 3; i <= n; i++) {
int tmp = b;
b = a + b;
a = tmp;
}
return b;
}
[Test]
public void Test() {
int n = 9;
int res = ClimbingStairsDP(n);
Console.WriteLine($"Количество способов подняться по лестнице из {n} ступеней = {res}");
res = ClimbingStairsDPComp(n);
Console.WriteLine($"Количество способов подняться по лестнице из {n} ступеней = {res}");
}
}
@@ -0,0 +1,71 @@
/**
* File: coin_change.cs
* Created Time: 2023-07-12
* Author: hpstory (hpstory1024@163.com)
*/
namespace hello_algo.chapter_dynamic_programming;
public class coin_change {
/* Размен монет: динамическое программирование */
int CoinChangeDP(int[] coins, int amt) {
int n = coins.Length;
int MAX = amt + 1;
// Инициализация таблицы dp
int[,] dp = new int[n + 1, amt + 1];
// Переход состояний: первая строка и первый столбец
for (int a = 1; a <= amt; a++) {
dp[0, a] = MAX;
}
// Переход состояний: остальные строки и столбцы
for (int i = 1; i <= n; i++) {
for (int 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;
}
/* Размен монет: динамическое программирование с оптимизацией памяти */
int CoinChangeDPComp(int[] coins, int amt) {
int n = coins.Length;
int MAX = amt + 1;
// Инициализация таблицы dp
int[] dp = new int[amt + 1];
Array.Fill(dp, MAX);
dp[0] = 0;
// Переход состояний
for (int i = 1; i <= n; i++) {
for (int 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;
}
[Test]
public void Test() {
int[] coins = [1, 2, 5];
int amt = 4;
// Динамическое программирование
int res = CoinChangeDP(coins, amt);
Console.WriteLine("Минимальное число монет для набора целевой суммы = " + res);
// Динамическое программирование с оптимизацией памяти
res = CoinChangeDPComp(coins, amt);
Console.WriteLine("Минимальное число монет для набора целевой суммы = " + res);
}
}
@@ -0,0 +1,68 @@
/**
* File: coin_change_ii.cs
* Created Time: 2023-07-12
* Author: hpstory (hpstory1024@163.com)
*/
namespace hello_algo.chapter_dynamic_programming;
public class coin_change_ii {
/* Размен монет II: динамическое программирование */
int CoinChangeIIDP(int[] coins, int amt) {
int n = coins.Length;
// Инициализация таблицы dp
int[,] dp = new int[n + 1, amt + 1];
// Инициализация первого столбца
for (int i = 0; i <= n; i++) {
dp[i, 0] = 1;
}
// Переход состояний
for (int i = 1; i <= n; i++) {
for (int 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];
}
/* Размен монет II: динамическое программирование с оптимизацией памяти */
int CoinChangeIIDPComp(int[] coins, int amt) {
int n = coins.Length;
// Инициализация таблицы dp
int[] dp = new int[amt + 1];
dp[0] = 1;
// Переход состояний
for (int i = 1; i <= n; i++) {
for (int 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];
}
[Test]
public void Test() {
int[] coins = [1, 2, 5];
int amt = 5;
// Динамическое программирование
int res = CoinChangeIIDP(coins, amt);
Console.WriteLine("Количество комбинаций монет для набора целевой суммы = " + res);
// Динамическое программирование с оптимизацией памяти
res = CoinChangeIIDPComp(coins, amt);
Console.WriteLine("Количество комбинаций монет для набора целевой суммы = " + res);
}
}
@@ -0,0 +1,141 @@
/**
* File: edit_distance.cs
* Created Time: 2023-07-14
* Author: hpstory (hpstory1024@163.com)
*/
namespace hello_algo.chapter_dynamic_programming;
public class edit_distance {
/* Редакционное расстояние: полный перебор */
int EditDistanceDFS(string s, string t, int i, int j) {
// Если s и t пусты, вернуть 0
if (i == 0 && j == 0)
return 0;
// Если s пусто, вернуть длину t
if (i == 0)
return j;
// Если t пусто, вернуть длину s
if (j == 0)
return i;
// Если два символа равны, сразу пропустить их
if (s[i - 1] == t[j - 1])
return EditDistanceDFS(s, t, i - 1, j - 1);
// Минимальное число шагов редактирования = минимальное число шагов для вставки, удаления и замены + 1
int insert = EditDistanceDFS(s, t, i, j - 1);
int delete = EditDistanceDFS(s, t, i - 1, j);
int replace = EditDistanceDFS(s, t, i - 1, j - 1);
// Вернуть минимальное число шагов редактирования
return Math.Min(Math.Min(insert, delete), replace) + 1;
}
/* Редакционное расстояние: поиск с мемоизацией */
int EditDistanceDFSMem(string s, string t, int[][] mem, int i, int j) {
// Если s и t пусты, вернуть 0
if (i == 0 && j == 0)
return 0;
// Если s пусто, вернуть длину t
if (i == 0)
return j;
// Если t пусто, вернуть длину s
if (j == 0)
return i;
// Если запись уже есть, сразу вернуть ее
if (mem[i][j] != -1)
return mem[i][j];
// Если два символа равны, сразу пропустить их
if (s[i - 1] == t[j - 1])
return EditDistanceDFSMem(s, t, mem, i - 1, j - 1);
// Минимальное число шагов редактирования = минимальное число шагов для вставки, удаления и замены + 1
int insert = EditDistanceDFSMem(s, t, mem, i, j - 1);
int delete = EditDistanceDFSMem(s, t, mem, i - 1, j);
int replace = EditDistanceDFSMem(s, t, mem, i - 1, j - 1);
// Сохранить и вернуть минимальное число шагов редактирования
mem[i][j] = Math.Min(Math.Min(insert, delete), replace) + 1;
return mem[i][j];
}
/* Редакционное расстояние: динамическое программирование */
int EditDistanceDP(string s, string t) {
int n = s.Length, m = t.Length;
int[,] dp = new int[n + 1, m + 1];
// Переход состояний: первая строка и первый столбец
for (int i = 1; i <= n; i++) {
dp[i, 0] = i;
}
for (int j = 1; j <= m; j++) {
dp[0, j] = j;
}
// Переход состояний: остальные строки и столбцы
for (int i = 1; i <= n; i++) {
for (int j = 1; j <= m; j++) {
if (s[i - 1] == t[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];
}
/* Редакционное расстояние: динамическое программирование с оптимизацией памяти */
int EditDistanceDPComp(string s, string t) {
int n = s.Length, m = t.Length;
int[] dp = new int[m + 1];
// Переход состояний: первая строка
for (int j = 1; j <= m; j++) {
dp[j] = j;
}
// Переход состояний: остальные строки
for (int i = 1; i <= n; i++) {
// Переход состояний: первый столбец
int leftup = dp[0]; // Временно сохранить dp[i-1, j-1]
dp[0] = i;
// Переход состояний: остальные столбцы
for (int j = 1; j <= m; j++) {
int temp = dp[j];
if (s[i - 1] == t[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];
}
[Test]
public void Test() {
string s = "bag";
string t = "pack";
int n = s.Length, m = t.Length;
// Полный перебор
int res = EditDistanceDFS(s, t, n, m);
Console.WriteLine("Чтобы преобразовать " + s + " в " + t + ", нужно минимум " + res + " шагов редактирования");
// Поиск с мемоизацией
int[][] mem = new int[n + 1][];
for (int i = 0; i <= n; i++) {
mem[i] = new int[m + 1];
Array.Fill(mem[i], -1);
}
res = EditDistanceDFSMem(s, t, mem, n, m);
Console.WriteLine("Чтобы преобразовать " + s + " в " + t + ", нужно минимум " + res + " шагов редактирования");
// Динамическое программирование
res = EditDistanceDP(s, t);
Console.WriteLine("Чтобы преобразовать " + s + " в " + t + ", нужно минимум " + res + " шагов редактирования");
// Динамическое программирование с оптимизацией памяти
res = EditDistanceDPComp(s, t);
Console.WriteLine("Чтобы преобразовать " + s + " в " + t + ", нужно минимум " + res + " шагов редактирования");
}
}
@@ -0,0 +1,118 @@
/**
* File: knapsack.cs
* Created Time: 2023-07-07
* Author: hpstory (hpstory1024@163.com)
*/
namespace hello_algo.chapter_dynamic_programming;
public class knapsack {
/* Рюкзак 0-1: полный перебор */
int KnapsackDFS(int[] weight, int[] val, int i, int c) {
// Если все предметы уже рассмотрены или в рюкзаке не осталось места, вернуть стоимость 0
if (i == 0 || c == 0) {
return 0;
}
// Если вместимость рюкзака превышена, можно только не класть предмет в рюкзак
if (weight[i - 1] > c) {
return KnapsackDFS(weight, val, i - 1, c);
}
// Вычислить максимальную стоимость для случаев, когда предмет i не кладут и кладут
int no = KnapsackDFS(weight, val, i - 1, c);
int yes = KnapsackDFS(weight, val, i - 1, c - weight[i - 1]) + val[i - 1];
// Вернуть вариант с большей стоимостью из двух возможных
return Math.Max(no, yes);
}
/* Рюкзак 0-1: поиск с мемоизацией */
int KnapsackDFSMem(int[] weight, int[] val, int[][] mem, int i, int c) {
// Если все предметы уже рассмотрены или в рюкзаке не осталось места, вернуть стоимость 0
if (i == 0 || c == 0) {
return 0;
}
// Если запись уже есть, вернуть сразу
if (mem[i][c] != -1) {
return mem[i][c];
}
// Если вместимость рюкзака превышена, можно только не класть предмет в рюкзак
if (weight[i - 1] > c) {
return KnapsackDFSMem(weight, val, mem, i - 1, c);
}
// Вычислить максимальную стоимость для случаев, когда предмет i не кладут и кладут
int no = KnapsackDFSMem(weight, val, mem, i - 1, c);
int yes = KnapsackDFSMem(weight, val, mem, i - 1, c - weight[i - 1]) + val[i - 1];
// Сохранить и вернуть вариант с большей стоимостью из двух решений
mem[i][c] = Math.Max(no, yes);
return mem[i][c];
}
/* Рюкзак 0-1: динамическое программирование */
int KnapsackDP(int[] weight, int[] val, int cap) {
int n = weight.Length;
// Инициализация таблицы dp
int[,] dp = new int[n + 1, cap + 1];
// Переход состояний
for (int i = 1; i <= n; i++) {
for (int c = 1; c <= cap; c++) {
if (weight[i - 1] > c) {
// Если вместимость рюкзака превышена, предмет i не выбирать
dp[i, c] = dp[i - 1, c];
} else {
// Большее из двух решений: не брать или взять предмет i
dp[i, c] = Math.Max(dp[i - 1, c - weight[i - 1]] + val[i - 1], dp[i - 1, c]);
}
}
}
return dp[n, cap];
}
/* Рюкзак 0-1: динамическое программирование с оптимизацией памяти */
int KnapsackDPComp(int[] weight, int[] val, int cap) {
int n = weight.Length;
// Инициализация таблицы dp
int[] dp = new int[cap + 1];
// Переход состояний
for (int i = 1; i <= n; i++) {
// Обход в обратном порядке
for (int c = cap; c > 0; c--) {
if (weight[i - 1] > c) {
// Если вместимость рюкзака превышена, предмет i не выбирать
dp[c] = dp[c];
} else {
// Большее из двух решений: не брать или взять предмет i
dp[c] = Math.Max(dp[c], dp[c - weight[i - 1]] + val[i - 1]);
}
}
}
return dp[cap];
}
[Test]
public void Test() {
int[] weight = [10, 20, 30, 40, 50];
int[] val = [50, 120, 150, 210, 240];
int cap = 50;
int n = weight.Length;
// Полный перебор
int res = KnapsackDFS(weight, val, n, cap);
Console.WriteLine("Максимальная стоимость предметов без превышения вместимости рюкзака = " + res);
// Поиск с мемоизацией
int[][] mem = new int[n + 1][];
for (int i = 0; i <= n; i++) {
mem[i] = new int[cap + 1];
Array.Fill(mem[i], -1);
}
res = KnapsackDFSMem(weight, val, mem, n, cap);
Console.WriteLine("Максимальная стоимость предметов без превышения вместимости рюкзака = " + res);
// Динамическое программирование
res = KnapsackDP(weight, val, cap);
Console.WriteLine("Максимальная стоимость предметов без превышения вместимости рюкзака = " + res);
// Динамическое программирование с оптимизацией памяти
res = KnapsackDPComp(weight, val, cap);
Console.WriteLine("Максимальная стоимость предметов без превышения вместимости рюкзака = " + res);
}
}
@@ -0,0 +1,53 @@
/**
* File: min_cost_climbing_stairs_dp.cs
* Created Time: 2023-06-30
* Author: hpstory (hpstory1024@163.com)
*/
namespace hello_algo.chapter_dynamic_programming;
public class min_cost_climbing_stairs_dp {
/* Минимальная стоимость подъема по лестнице: динамическое программирование */
int MinCostClimbingStairsDP(int[] cost) {
int n = cost.Length - 1;
if (n == 1 || n == 2)
return cost[n];
// Инициализация таблицы dp для хранения решений подзадач
int[] dp = new int[n + 1];
// Начальное состояние: заранее задать решения наименьших подзадач
dp[1] = cost[1];
dp[2] = cost[2];
// Переход состояний: постепенное решение больших подзадач через меньшие
for (int i = 3; i <= n; i++) {
dp[i] = Math.Min(dp[i - 1], dp[i - 2]) + cost[i];
}
return dp[n];
}
/* Минимальная стоимость подъема по лестнице: динамическое программирование с оптимизацией памяти */
int MinCostClimbingStairsDPComp(int[] cost) {
int n = cost.Length - 1;
if (n == 1 || n == 2)
return cost[n];
int a = cost[1], b = cost[2];
for (int i = 3; i <= n; i++) {
int tmp = b;
b = Math.Min(a, tmp) + cost[i];
a = tmp;
}
return b;
}
[Test]
public void Test() {
int[] cost = [0, 1, 10, 1, 1, 1, 10, 1, 1, 10, 1];
Console.WriteLine("Список стоимостей ступеней:");
PrintUtil.PrintList(cost);
int res = MinCostClimbingStairsDP(cost);
Console.WriteLine($"Минимальная стоимость подъема по лестнице = {res}");
res = MinCostClimbingStairsDPComp(cost);
Console.WriteLine($"Минимальная стоимость подъема по лестнице = {res}");
}
}
@@ -0,0 +1,127 @@
/**
* File: min_path_sum.cs
* Created Time: 2023-07-10
* Author: hpstory (hpstory1024@163.com)
*/
namespace hello_algo.chapter_dynamic_programming;
public class min_path_sum {
/* Минимальная сумма пути: полный перебор */
int MinPathSumDFS(int[][] grid, int i, int j) {
// Если это верхняя левая ячейка, завершить поиск
if (i == 0 && j == 0) {
return grid[0][0];
}
// Если индексы строки или столбца выходят за границы, вернуть стоимость +∞
if (i < 0 || j < 0) {
return int.MaxValue;
}
// Вычислить минимальную стоимость пути из левого верхнего угла до (i-1, j) и (i, j-1)
int up = MinPathSumDFS(grid, i - 1, j);
int left = MinPathSumDFS(grid, i, j - 1);
// Вернуть минимальную стоимость пути из левого верхнего угла до (i, j)
return Math.Min(left, up) + grid[i][j];
}
/* Минимальная сумма пути: поиск с мемоизацией */
int MinPathSumDFSMem(int[][] grid, int[][] mem, int i, int j) {
// Если это верхняя левая ячейка, завершить поиск
if (i == 0 && j == 0) {
return grid[0][0];
}
// Если индексы строки или столбца выходят за границы, вернуть стоимость +∞
if (i < 0 || j < 0) {
return int.MaxValue;
}
// Если запись уже есть, вернуть сразу
if (mem[i][j] != -1) {
return mem[i][j];
}
// Минимальная стоимость пути для левой и верхней ячеек
int up = MinPathSumDFSMem(grid, mem, i - 1, j);
int left = MinPathSumDFSMem(grid, mem, i, j - 1);
// Сохранить и вернуть минимальную стоимость пути из левого верхнего угла до (i, j)
mem[i][j] = Math.Min(left, up) + grid[i][j];
return mem[i][j];
}
/* Минимальная сумма пути: динамическое программирование */
int MinPathSumDP(int[][] grid) {
int n = grid.Length, m = grid[0].Length;
// Инициализация таблицы dp
int[,] dp = new int[n, m];
dp[0, 0] = grid[0][0];
// Переход состояний: первая строка
for (int j = 1; j < m; j++) {
dp[0, j] = dp[0, j - 1] + grid[0][j];
}
// Переход состояний: первый столбец
for (int i = 1; i < n; i++) {
dp[i, 0] = dp[i - 1, 0] + grid[i][0];
}
// Переход состояний: остальные строки и столбцы
for (int i = 1; i < n; i++) {
for (int 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];
}
/* Минимальная сумма пути: динамическое программирование с оптимизацией памяти */
int MinPathSumDPComp(int[][] grid) {
int n = grid.Length, m = grid[0].Length;
// Инициализация таблицы dp
int[] dp = new int[m];
dp[0] = grid[0][0];
// Переход состояний: первая строка
for (int j = 1; j < m; j++) {
dp[j] = dp[j - 1] + grid[0][j];
}
// Переход состояний: остальные строки
for (int i = 1; i < n; i++) {
// Переход состояний: первый столбец
dp[0] = dp[0] + grid[i][0];
// Переход состояний: остальные столбцы
for (int j = 1; j < m; j++) {
dp[j] = Math.Min(dp[j - 1], dp[j]) + grid[i][j];
}
}
return dp[m - 1];
}
[Test]
public void Test() {
int[][] grid =
[
[1, 3, 1, 5],
[2, 2, 4, 2],
[5, 3, 2, 1],
[4, 3, 5, 2]
];
int n = grid.Length, m = grid[0].Length;
// Полный перебор
int res = MinPathSumDFS(grid, n - 1, m - 1);
Console.WriteLine("Минимальная сумма пути из левого верхнего угла в правый нижний = " + res);
// Поиск с мемоизацией
int[][] mem = new int[n][];
for (int i = 0; i < n; i++) {
mem[i] = new int[m];
Array.Fill(mem[i], -1);
}
res = MinPathSumDFSMem(grid, mem, n - 1, m - 1);
Console.WriteLine("Минимальная сумма пути из левого верхнего угла в правый нижний = " + res);
// Динамическое программирование
res = MinPathSumDP(grid);
Console.WriteLine("Минимальная сумма пути из левого верхнего угла в правый нижний = " + res);
// Динамическое программирование с оптимизацией памяти
res = MinPathSumDPComp(grid);
Console.WriteLine("Минимальная сумма пути из левого верхнего угла в правый нижний = " + res);
}
}
@@ -0,0 +1,64 @@
/**
* File: unbounded_knapsack.cs
* Created Time: 2023-07-12
* Author: hpstory (hpstory1024@163.com)
*/
namespace hello_algo.chapter_dynamic_programming;
public class unbounded_knapsack {
/* Полный рюкзак: динамическое программирование */
int UnboundedKnapsackDP(int[] wgt, int[] val, int cap) {
int n = wgt.Length;
// Инициализация таблицы dp
int[,] dp = new int[n + 1, cap + 1];
// Переход состояний
for (int i = 1; i <= n; i++) {
for (int 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];
}
/* Полный рюкзак: динамическое программирование с оптимизацией памяти */
int UnboundedKnapsackDPComp(int[] wgt, int[] val, int cap) {
int n = wgt.Length;
// Инициализация таблицы dp
int[] dp = new int[cap + 1];
// Переход состояний
for (int i = 1; i <= n; i++) {
for (int 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];
}
[Test]
public void Test() {
int[] wgt = [1, 2, 3];
int[] val = [5, 11, 15];
int cap = 4;
// Динамическое программирование
int res = UnboundedKnapsackDP(wgt, val, cap);
Console.WriteLine("Максимальная стоимость предметов без превышения вместимости рюкзака = " + res);
// Динамическое программирование с оптимизацией памяти
res = UnboundedKnapsackDPComp(wgt, val, cap);
Console.WriteLine("Максимальная стоимость предметов без превышения вместимости рюкзака = " + res);
}
}