mirror of
https://github.com/krahets/hello-algo.git
synced 2026-07-14 08:06:06 +00:00
build
This commit is contained in:
@@ -0,0 +1,580 @@
|
||||
---
|
||||
comments: true
|
||||
---
|
||||
|
||||
# 15.2 Задача о дробном рюкзаке
|
||||
|
||||
!!! question
|
||||
|
||||
Дано $n$ предметов. Вес предмета $i$ равен $wgt[i-1]$, ценность равна $val[i-1]$, также дан рюкзак вместимостью $cap$. Каждый предмет можно выбрать только один раз, **но разрешается взять лишь часть предмета, а ценность вычисляется пропорционально взятому весу**. Требуется найти максимальную ценность предметов в рюкзаке при ограниченной вместимости. Пример показан на рисунке 15-3.
|
||||
|
||||
{ class="animation-figure" }
|
||||
|
||||
<p align="center"> Рисунок 15-3 Пример данных для задачи о дробном рюкзаке </p>
|
||||
|
||||
Задача о дробном рюкзаке в целом очень похожа на задачу о рюкзаке 0-1: состояние включает текущий предмет $i$ и вместимость $c$, а цель состоит в нахождении максимальной ценности при заданной вместимости рюкзака.
|
||||
|
||||
Отличие в том, что здесь разрешено брать только часть предмета. Как показано на рисунке 15-4, **мы можем произвольно делить предмет и вычислять соответствующую ценность пропорционально весу**.
|
||||
|
||||
1. Для предмета $i$ его ценность на единицу веса равна $val[i-1] / wgt[i-1]$, сокращенно - удельная ценность.
|
||||
2. Если взять часть предмета $i$ весом $w$, то ценность рюкзака увеличится на $w \times val[i-1] / wgt[i-1]$.
|
||||
|
||||
{ class="animation-figure" }
|
||||
|
||||
<p align="center"> Рисунок 15-4 Ценность предмета на единицу веса </p>
|
||||
|
||||
### 1. Определение жадной стратегии
|
||||
|
||||
Максимизация общей ценности предметов в рюкзаке **по сути равносильна максимизации ценности на единицу веса**. Отсюда естественно выводится следующая жадная стратегия.
|
||||
|
||||
1. Отсортировать предметы по убыванию удельной ценности.
|
||||
2. Перебирать все предметы и **на каждом шаге жадно выбирать предмет с наибольшей удельной ценностью**.
|
||||
3. Если оставшейся вместимости рюкзака недостаточно, взять часть текущего предмета, чтобы заполнить рюкзак.
|
||||
|
||||
{ class="animation-figure" }
|
||||
|
||||
<p align="center"> Рисунок 15-5 Жадная стратегия для задачи о дробном рюкзаке </p>
|
||||
|
||||
### 2. Код реализации
|
||||
|
||||
Мы вводим класс `Item`, чтобы можно было сортировать предметы по удельной ценности. Далее циклически выполняем жадный выбор и, когда рюкзак заполнен, выходим и возвращаем ответ:
|
||||
|
||||
=== "Python"
|
||||
|
||||
```python title="fractional_knapsack.py"
|
||||
class Item:
|
||||
"""Предмет"""
|
||||
|
||||
def __init__(self, w: int, v: int):
|
||||
self.w = w # Вес предмета
|
||||
self.v = v # Стоимость предмета
|
||||
|
||||
def fractional_knapsack(wgt: list[int], val: list[int], cap: int) -> int:
|
||||
"""Дробный рюкзак: жадный алгоритм"""
|
||||
# Создать список предметов с двумя свойствами: вес и стоимость
|
||||
items = [Item(w, v) for w, v in zip(wgt, val)]
|
||||
# Отсортировать по удельной стоимости item.v / item.w в порядке убывания
|
||||
items.sort(key=lambda item: item.v / item.w, reverse=True)
|
||||
# Циклический жадный выбор
|
||||
res = 0
|
||||
for item in items:
|
||||
if item.w <= cap:
|
||||
# Если оставшейся вместимости достаточно, положить в рюкзак текущий предмет целиком
|
||||
res += item.v
|
||||
cap -= item.w
|
||||
else:
|
||||
# Если оставшейся вместимости недостаточно, положить в рюкзак часть текущего предмета
|
||||
res += (item.v / item.w) * cap
|
||||
# Свободной вместимости больше не осталось, поэтому выйти из цикла
|
||||
break
|
||||
return res
|
||||
```
|
||||
|
||||
=== "C++"
|
||||
|
||||
```cpp title="fractional_knapsack.cpp"
|
||||
/* Предмет */
|
||||
class Item {
|
||||
public:
|
||||
int w; // Вес предмета
|
||||
int v; // Стоимость предмета
|
||||
|
||||
Item(int w, int v) : w(w), v(v) {
|
||||
}
|
||||
};
|
||||
|
||||
/* Дробный рюкзак: жадный алгоритм */
|
||||
double fractionalKnapsack(vector<int> &wgt, vector<int> &val, int cap) {
|
||||
// Создать список предметов с двумя свойствами: вес и стоимость
|
||||
vector<Item> items;
|
||||
for (int i = 0; i < wgt.size(); i++) {
|
||||
items.push_back(Item(wgt[i], val[i]));
|
||||
}
|
||||
// Отсортировать по удельной стоимости item.v / item.w в порядке убывания
|
||||
sort(items.begin(), items.end(), [](Item &a, Item &b) { return (double)a.v / a.w > (double)b.v / b.w; });
|
||||
// Циклический жадный выбор
|
||||
double res = 0;
|
||||
for (auto &item : items) {
|
||||
if (item.w <= cap) {
|
||||
// Если оставшейся вместимости достаточно, положить в рюкзак текущий предмет целиком
|
||||
res += item.v;
|
||||
cap -= item.w;
|
||||
} else {
|
||||
// Если оставшейся вместимости недостаточно, положить в рюкзак часть текущего предмета
|
||||
res += (double)item.v / item.w * cap;
|
||||
// Свободной вместимости больше не осталось, поэтому выйти из цикла
|
||||
break;
|
||||
}
|
||||
}
|
||||
return res;
|
||||
}
|
||||
```
|
||||
|
||||
=== "Java"
|
||||
|
||||
```java title="fractional_knapsack.java"
|
||||
/* Предмет */
|
||||
class Item {
|
||||
int w; // Вес предмета
|
||||
int v; // Стоимость предмета
|
||||
|
||||
public Item(int w, int v) {
|
||||
this.w = w;
|
||||
this.v = v;
|
||||
}
|
||||
}
|
||||
|
||||
/* Дробный рюкзак: жадный алгоритм */
|
||||
double fractionalKnapsack(int[] wgt, int[] val, int cap) {
|
||||
// Создать список предметов с двумя свойствами: вес и стоимость
|
||||
Item[] items = new Item[wgt.length];
|
||||
for (int i = 0; i < wgt.length; i++) {
|
||||
items[i] = new Item(wgt[i], val[i]);
|
||||
}
|
||||
// Отсортировать по удельной стоимости item.v / item.w в порядке убывания
|
||||
Arrays.sort(items, Comparator.comparingDouble(item -> -((double) item.v / item.w)));
|
||||
// Циклический жадный выбор
|
||||
double res = 0;
|
||||
for (Item item : items) {
|
||||
if (item.w <= cap) {
|
||||
// Если оставшейся вместимости достаточно, положить в рюкзак текущий предмет целиком
|
||||
res += item.v;
|
||||
cap -= item.w;
|
||||
} else {
|
||||
// Если оставшейся вместимости недостаточно, положить в рюкзак часть текущего предмета
|
||||
res += (double) item.v / item.w * cap;
|
||||
// Свободной вместимости больше не осталось, поэтому выйти из цикла
|
||||
break;
|
||||
}
|
||||
}
|
||||
return res;
|
||||
}
|
||||
```
|
||||
|
||||
=== "C#"
|
||||
|
||||
```csharp title="fractional_knapsack.cs"
|
||||
/* Предмет */
|
||||
class Item(int w, int v) {
|
||||
public int w = w; // Вес предмета
|
||||
public int v = v; // Стоимость предмета
|
||||
}
|
||||
|
||||
/* Дробный рюкзак: жадный алгоритм */
|
||||
double FractionalKnapsack(int[] wgt, int[] val, int cap) {
|
||||
// Создать список предметов с двумя свойствами: вес и стоимость
|
||||
Item[] items = new Item[wgt.Length];
|
||||
for (int i = 0; i < wgt.Length; i++) {
|
||||
items[i] = new Item(wgt[i], val[i]);
|
||||
}
|
||||
// Отсортировать по удельной стоимости item.v / item.w в порядке убывания
|
||||
Array.Sort(items, (x, y) => (y.v / y.w).CompareTo(x.v / x.w));
|
||||
// Циклический жадный выбор
|
||||
double res = 0;
|
||||
foreach (Item item in items) {
|
||||
if (item.w <= cap) {
|
||||
// Если оставшейся вместимости достаточно, положить в рюкзак текущий предмет целиком
|
||||
res += item.v;
|
||||
cap -= item.w;
|
||||
} else {
|
||||
// Если оставшейся вместимости недостаточно, положить в рюкзак часть текущего предмета
|
||||
res += (double)item.v / item.w * cap;
|
||||
// Свободной вместимости больше не осталось, поэтому выйти из цикла
|
||||
break;
|
||||
}
|
||||
}
|
||||
return res;
|
||||
}
|
||||
```
|
||||
|
||||
=== "Go"
|
||||
|
||||
```go title="fractional_knapsack.go"
|
||||
/* Предмет */
|
||||
type Item struct {
|
||||
w int // Вес предмета
|
||||
v int // Стоимость предмета
|
||||
}
|
||||
|
||||
/* Дробный рюкзак: жадный алгоритм */
|
||||
func fractionalKnapsack(wgt []int, val []int, cap int) float64 {
|
||||
// Создать список предметов с двумя свойствами: вес и стоимость
|
||||
items := make([]Item, len(wgt))
|
||||
for i := 0; i < len(wgt); i++ {
|
||||
items[i] = Item{wgt[i], val[i]}
|
||||
}
|
||||
// Отсортировать по удельной стоимости item.v / item.w в порядке убывания
|
||||
sort.Slice(items, func(i, j int) bool {
|
||||
return float64(items[i].v)/float64(items[i].w) > float64(items[j].v)/float64(items[j].w)
|
||||
})
|
||||
// Циклический жадный выбор
|
||||
res := 0.0
|
||||
for _, item := range items {
|
||||
if item.w <= cap {
|
||||
// Если оставшейся вместимости достаточно, положить в рюкзак текущий предмет целиком
|
||||
res += float64(item.v)
|
||||
cap -= item.w
|
||||
} else {
|
||||
// Если оставшейся вместимости недостаточно, положить в рюкзак часть текущего предмета
|
||||
res += float64(item.v) / float64(item.w) * float64(cap)
|
||||
// Свободной вместимости больше не осталось, поэтому выйти из цикла
|
||||
break
|
||||
}
|
||||
}
|
||||
return res
|
||||
}
|
||||
```
|
||||
|
||||
=== "Swift"
|
||||
|
||||
```swift title="fractional_knapsack.swift"
|
||||
/* Предмет */
|
||||
class Item {
|
||||
var w: Int // Вес предмета
|
||||
var v: Int // Стоимость предмета
|
||||
|
||||
init(w: Int, v: Int) {
|
||||
self.w = w
|
||||
self.v = v
|
||||
}
|
||||
}
|
||||
|
||||
/* Дробный рюкзак: жадный алгоритм */
|
||||
func fractionalKnapsack(wgt: [Int], val: [Int], cap: Int) -> Double {
|
||||
// Создать список предметов с двумя свойствами: вес и стоимость
|
||||
var items = zip(wgt, val).map { Item(w: $0, v: $1) }
|
||||
// Отсортировать по удельной стоимости item.v / item.w в порядке убывания
|
||||
items.sort { -(Double($0.v) / Double($0.w)) < -(Double($1.v) / Double($1.w)) }
|
||||
// Циклический жадный выбор
|
||||
var res = 0.0
|
||||
var cap = cap
|
||||
for item in items {
|
||||
if item.w <= cap {
|
||||
// Если оставшейся вместимости достаточно, положить в рюкзак текущий предмет целиком
|
||||
res += Double(item.v)
|
||||
cap -= item.w
|
||||
} else {
|
||||
// Если оставшейся вместимости недостаточно, положить в рюкзак часть текущего предмета
|
||||
res += Double(item.v) / Double(item.w) * Double(cap)
|
||||
// Свободной вместимости больше не осталось, поэтому выйти из цикла
|
||||
break
|
||||
}
|
||||
}
|
||||
return res
|
||||
}
|
||||
```
|
||||
|
||||
=== "JS"
|
||||
|
||||
```javascript title="fractional_knapsack.js"
|
||||
/* Предмет */
|
||||
class Item {
|
||||
constructor(w, v) {
|
||||
this.w = w; // Вес предмета
|
||||
this.v = v; // Стоимость предмета
|
||||
}
|
||||
}
|
||||
|
||||
/* Дробный рюкзак: жадный алгоритм */
|
||||
function fractionalKnapsack(wgt, val, cap) {
|
||||
// Создать список предметов с двумя свойствами: вес и стоимость
|
||||
const items = wgt.map((w, i) => new Item(w, val[i]));
|
||||
// Отсортировать по удельной стоимости item.v / item.w в порядке убывания
|
||||
items.sort((a, b) => b.v / b.w - a.v / a.w);
|
||||
// Циклический жадный выбор
|
||||
let res = 0;
|
||||
for (const item of items) {
|
||||
if (item.w <= cap) {
|
||||
// Если оставшейся вместимости достаточно, положить в рюкзак текущий предмет целиком
|
||||
res += item.v;
|
||||
cap -= item.w;
|
||||
} else {
|
||||
// Если оставшейся вместимости недостаточно, положить в рюкзак часть текущего предмета
|
||||
res += (item.v / item.w) * cap;
|
||||
// Свободной вместимости больше не осталось, поэтому выйти из цикла
|
||||
break;
|
||||
}
|
||||
}
|
||||
return res;
|
||||
}
|
||||
```
|
||||
|
||||
=== "TS"
|
||||
|
||||
```typescript title="fractional_knapsack.ts"
|
||||
/* Предмет */
|
||||
class Item {
|
||||
w: number; // Вес предмета
|
||||
v: number; // Стоимость предмета
|
||||
|
||||
constructor(w: number, v: number) {
|
||||
this.w = w;
|
||||
this.v = v;
|
||||
}
|
||||
}
|
||||
|
||||
/* Дробный рюкзак: жадный алгоритм */
|
||||
function fractionalKnapsack(wgt: number[], val: number[], cap: number): number {
|
||||
// Создать список предметов с двумя свойствами: вес и стоимость
|
||||
const items: Item[] = wgt.map((w, i) => new Item(w, val[i]));
|
||||
// Отсортировать по удельной стоимости item.v / item.w в порядке убывания
|
||||
items.sort((a, b) => b.v / b.w - a.v / a.w);
|
||||
// Циклический жадный выбор
|
||||
let res = 0;
|
||||
for (const item of items) {
|
||||
if (item.w <= cap) {
|
||||
// Если оставшейся вместимости достаточно, положить в рюкзак текущий предмет целиком
|
||||
res += item.v;
|
||||
cap -= item.w;
|
||||
} else {
|
||||
// Если оставшейся вместимости недостаточно, положить в рюкзак часть текущего предмета
|
||||
res += (item.v / item.w) * cap;
|
||||
// Свободной вместимости больше не осталось, поэтому выйти из цикла
|
||||
break;
|
||||
}
|
||||
}
|
||||
return res;
|
||||
}
|
||||
```
|
||||
|
||||
=== "Dart"
|
||||
|
||||
```dart title="fractional_knapsack.dart"
|
||||
/* Предмет */
|
||||
class Item {
|
||||
int w; // Вес предмета
|
||||
int v; // Стоимость предмета
|
||||
|
||||
Item(this.w, this.v);
|
||||
}
|
||||
|
||||
/* Дробный рюкзак: жадный алгоритм */
|
||||
double fractionalKnapsack(List<int> wgt, List<int> val, int cap) {
|
||||
// Создать список предметов с двумя свойствами: вес и стоимость
|
||||
List<Item> items = List.generate(wgt.length, (i) => Item(wgt[i], val[i]));
|
||||
// Отсортировать по удельной стоимости item.v / item.w в порядке убывания
|
||||
items.sort((a, b) => (b.v / b.w).compareTo(a.v / a.w));
|
||||
// Циклический жадный выбор
|
||||
double res = 0;
|
||||
for (Item item in items) {
|
||||
if (item.w <= cap) {
|
||||
// Если оставшейся вместимости достаточно, положить в рюкзак текущий предмет целиком
|
||||
res += item.v;
|
||||
cap -= item.w;
|
||||
} else {
|
||||
// Если оставшейся вместимости недостаточно, положить в рюкзак часть текущего предмета
|
||||
res += item.v / item.w * cap;
|
||||
// Свободной вместимости больше не осталось, поэтому выйти из цикла
|
||||
break;
|
||||
}
|
||||
}
|
||||
return res;
|
||||
}
|
||||
```
|
||||
|
||||
=== "Rust"
|
||||
|
||||
```rust title="fractional_knapsack.rs"
|
||||
/* Предмет */
|
||||
struct Item {
|
||||
w: i32, // Вес предмета
|
||||
v: i32, // Стоимость предмета
|
||||
}
|
||||
|
||||
impl Item {
|
||||
fn new(w: i32, v: i32) -> Self {
|
||||
Self { w, v }
|
||||
}
|
||||
}
|
||||
|
||||
/* Дробный рюкзак: жадный алгоритм */
|
||||
fn fractional_knapsack(wgt: &[i32], val: &[i32], mut cap: i32) -> f64 {
|
||||
// Создать список предметов с двумя свойствами: вес и стоимость
|
||||
let mut items = wgt
|
||||
.iter()
|
||||
.zip(val.iter())
|
||||
.map(|(&w, &v)| Item::new(w, v))
|
||||
.collect::<Vec<Item>>();
|
||||
// Отсортировать по удельной стоимости item.v / item.w в порядке убывания
|
||||
items.sort_by(|a, b| {
|
||||
(b.v as f64 / b.w as f64)
|
||||
.partial_cmp(&(a.v as f64 / a.w as f64))
|
||||
.unwrap()
|
||||
});
|
||||
// Циклический жадный выбор
|
||||
let mut res = 0.0;
|
||||
for item in &items {
|
||||
if item.w <= cap {
|
||||
// Если оставшейся вместимости достаточно, положить в рюкзак текущий предмет целиком
|
||||
res += item.v as f64;
|
||||
cap -= item.w;
|
||||
} else {
|
||||
// Если оставшейся вместимости недостаточно, положить в рюкзак часть текущего предмета
|
||||
res += item.v as f64 / item.w as f64 * cap as f64;
|
||||
// Свободной вместимости больше не осталось, поэтому выйти из цикла
|
||||
break;
|
||||
}
|
||||
}
|
||||
res
|
||||
}
|
||||
```
|
||||
|
||||
=== "C"
|
||||
|
||||
```c title="fractional_knapsack.c"
|
||||
/* Предмет */
|
||||
typedef struct {
|
||||
int w; // Вес предмета
|
||||
int v; // Стоимость предмета
|
||||
} Item;
|
||||
|
||||
/* Дробный рюкзак: жадный алгоритм */
|
||||
float fractionalKnapsack(int wgt[], int val[], int itemCount, int cap) {
|
||||
// Создать список предметов с двумя свойствами: вес и стоимость
|
||||
Item *items = malloc(sizeof(Item) * itemCount);
|
||||
for (int i = 0; i < itemCount; i++) {
|
||||
items[i] = (Item){.w = wgt[i], .v = val[i]};
|
||||
}
|
||||
// Отсортировать по удельной стоимости item.v / item.w в порядке убывания
|
||||
qsort(items, (size_t)itemCount, sizeof(Item), sortByValueDensity);
|
||||
// Циклический жадный выбор
|
||||
float res = 0.0;
|
||||
for (int i = 0; i < itemCount; i++) {
|
||||
if (items[i].w <= cap) {
|
||||
// Если оставшейся вместимости достаточно, положить в рюкзак текущий предмет целиком
|
||||
res += items[i].v;
|
||||
cap -= items[i].w;
|
||||
} else {
|
||||
// Если оставшейся вместимости недостаточно, положить в рюкзак часть текущего предмета
|
||||
res += (float)cap / items[i].w * items[i].v;
|
||||
cap = 0;
|
||||
break;
|
||||
}
|
||||
}
|
||||
free(items);
|
||||
return res;
|
||||
}
|
||||
```
|
||||
|
||||
=== "Kotlin"
|
||||
|
||||
```kotlin title="fractional_knapsack.kt"
|
||||
/* Предмет */
|
||||
class Item(
|
||||
val w: Int, // Предмет
|
||||
val v: Int // Стоимость предмета
|
||||
)
|
||||
|
||||
/* Дробный рюкзак: жадный алгоритм */
|
||||
fun fractionalKnapsack(wgt: IntArray, _val: IntArray, c: Int): Double {
|
||||
// Создать список предметов с двумя свойствами: вес и стоимость
|
||||
var cap = c
|
||||
val items = arrayOfNulls<Item>(wgt.size)
|
||||
for (i in wgt.indices) {
|
||||
items[i] = Item(wgt[i], _val[i])
|
||||
}
|
||||
// Отсортировать по удельной стоимости item.v / item.w в порядке убывания
|
||||
items.sortBy { item: Item? -> -(item!!.v.toDouble() / item.w) }
|
||||
// Циклический жадный выбор
|
||||
var res = 0.0
|
||||
for (item in items) {
|
||||
if (item!!.w <= cap) {
|
||||
// Если оставшейся вместимости достаточно, положить в рюкзак текущий предмет целиком
|
||||
res += item.v
|
||||
cap -= item.w
|
||||
} else {
|
||||
// Если оставшейся вместимости недостаточно, положить в рюкзак часть текущего предмета
|
||||
res += item.v.toDouble() / item.w * cap
|
||||
// Свободной вместимости больше не осталось, поэтому выйти из цикла
|
||||
break
|
||||
}
|
||||
}
|
||||
return res
|
||||
}
|
||||
```
|
||||
|
||||
=== "Ruby"
|
||||
|
||||
```ruby title="fractional_knapsack.rb"
|
||||
=begin
|
||||
File: fractional_knapsack.rb
|
||||
Created Time: 2024-05-07
|
||||
Author: Xuan Khoa Tu Nguyen (ngxktuzkai2000@gmail.com)
|
||||
=end
|
||||
|
||||
# ## Предмет ###
|
||||
class Item
|
||||
attr_accessor :w # Вес предмета
|
||||
attr_accessor :v # Стоимость предмета
|
||||
|
||||
def initialize(w, v)
|
||||
@w = w
|
||||
@v = v
|
||||
end
|
||||
end
|
||||
|
||||
=begin
|
||||
File: fractional_knapsack.rb
|
||||
Created Time: 2024-05-07
|
||||
Author: Xuan Khoa Tu Nguyen (ngxktuzkai2000@gmail.com)
|
||||
=end
|
||||
|
||||
# ## Предмет ###
|
||||
class Item
|
||||
attr_accessor :w # Вес предмета
|
||||
attr_accessor :v # Стоимость предмета
|
||||
|
||||
def initialize(w, v)
|
||||
@w = w
|
||||
@v = v
|
||||
end
|
||||
end
|
||||
|
||||
# ## Дробный рюкзак: жадный алгоритм ###
|
||||
def fractional_knapsack(wgt, val, cap)
|
||||
# Создать список предметов с двумя свойствами: вес и стоимость
|
||||
items = wgt.each_with_index.map { |w, i| Item.new(w, val[i]) }
|
||||
# Отсортировать по удельной стоимости item.v / item.w в порядке убывания
|
||||
items.sort! { |a, b| (b.v.to_f / b.w) <=> (a.v.to_f / a.w) }
|
||||
# Циклический жадный выбор
|
||||
res = 0
|
||||
for item in items
|
||||
if item.w <= cap
|
||||
# Если оставшейся вместимости достаточно, положить в рюкзак текущий предмет целиком
|
||||
res += item.v
|
||||
cap -= item.w
|
||||
else
|
||||
# Если оставшейся вместимости недостаточно, положить в рюкзак часть текущего предмета
|
||||
res += (item.v.to_f / item.w) * cap
|
||||
# Свободной вместимости больше не осталось, поэтому выйти из цикла
|
||||
break
|
||||
end
|
||||
end
|
||||
res
|
||||
end
|
||||
```
|
||||
|
||||
??? pythontutor "Визуализация кода"
|
||||
|
||||
<div style="height: 549px; width: 100%;"><iframe class="pythontutor-iframe" src="https://pythontutor.com/iframe-embed.html#code=class%20Item%3A%0A%20%20%20%20%22%22%22%D0%9F%D1%80%D0%B5%D0%B4%D0%BC%D0%B5%D1%82%22%22%22%0A%20%20%20%20def%20__init__%28self%2C%20w%3A%20int%2C%20v%3A%20int%29%3A%0A%20%20%20%20%20%20%20%20self.w%20%3D%20w%20%20%23%20%D0%92%D0%B5%D1%81%20%D0%BF%D1%80%D0%B5%D0%B4%D0%BC%D0%B5%D1%82%D0%B0%0A%20%20%20%20%20%20%20%20self.v%20%3D%20v%20%20%23%20%D0%A1%D1%82%D0%BE%D0%B8%D0%BC%D0%BE%D1%81%D1%82%D1%8C%20%D0%BF%D1%80%D0%B5%D0%B4%D0%BC%D0%B5%D1%82%D0%B0%0A%0Adef%20fractional_knapsack%28wgt%3A%20list%5Bint%5D%2C%20val%3A%20list%5Bint%5D%2C%20cap%3A%20int%29%20-%3E%20int%3A%0A%20%20%20%20%22%22%22%D0%94%D1%80%D0%BE%D0%B1%D0%BD%D1%8B%D0%B9%20%D1%80%D1%8E%D0%BA%D0%B7%D0%B0%D0%BA%3A%20%D0%B6%D0%B0%D0%B4%D0%BD%D1%8B%D0%B9%20%D0%B0%D0%BB%D0%B3%D0%BE%D1%80%D0%B8%D1%82%D0%BC%22%22%22%0A%20%20%20%20%23%20%D0%A1%D0%BE%D0%B7%D0%B4%D0%B0%D1%82%D1%8C%20%D1%81%D0%BF%D0%B8%D1%81%D0%BE%D0%BA%20%D0%BF%D1%80%D0%B5%D0%B4%D0%BC%D0%B5%D1%82%D0%BE%D0%B2%20%D1%81%20%D0%B4%D0%B2%D1%83%D0%BC%D1%8F%20%D1%81%D0%B2%D0%BE%D0%B9%D1%81%D1%82%D0%B2%D0%B0%D0%BC%D0%B8%3A%20%D0%B2%D0%B5%D1%81%20%D0%B8%20%D1%81%D1%82%D0%BE%D0%B8%D0%BC%D0%BE%D1%81%D1%82%D1%8C%0A%20%20%20%20items%20%3D%20%5BItem%28w%2C%20v%29%20for%20w%2C%20v%20in%20zip%28wgt%2C%20val%29%5D%0A%20%20%20%20%23%20%D0%9E%D1%82%D1%81%D0%BE%D1%80%D1%82%D0%B8%D1%80%D0%BE%D0%B2%D0%B0%D1%82%D1%8C%20%D0%BF%D0%BE%20%D1%83%D0%B4%D0%B5%D0%BB%D1%8C%D0%BD%D0%BE%D0%B9%20%D1%81%D1%82%D0%BE%D0%B8%D0%BC%D0%BE%D1%81%D1%82%D0%B8%20item.v%20%2F%20item.w%20%D0%B2%20%D0%BF%D0%BE%D1%80%D1%8F%D0%B4%D0%BA%D0%B5%20%D1%83%D0%B1%D1%8B%D0%B2%D0%B0%D0%BD%D0%B8%D1%8F%0A%20%20%20%20items.sort%28key%3Dlambda%20item%3A%20item.v%20%2F%20item.w%2C%20reverse%3DTrue%29%0A%20%20%20%20%23%20%D0%A6%D0%B8%D0%BA%D0%BB%D0%B8%D1%87%D0%B5%D1%81%D0%BA%D0%B8%D0%B9%20%D0%B6%D0%B0%D0%B4%D0%BD%D1%8B%D0%B9%20%D0%B2%D1%8B%D0%B1%D0%BE%D1%80%0A%20%20%20%20res%20%3D%200%0A%20%20%20%20for%20item%20in%20items%3A%0A%20%20%20%20%20%20%20%20if%20item.w%20%3C%3D%20cap%3A%0A%20%20%20%20%20%20%20%20%20%20%20%20%23%20%D0%95%D1%81%D0%BB%D0%B8%20%D0%BE%D1%81%D1%82%D0%B0%D0%B2%D1%88%D0%B5%D0%B9%D1%81%D1%8F%20%D0%B2%D0%BC%D0%B5%D1%81%D1%82%D0%B8%D0%BC%D0%BE%D1%81%D1%82%D0%B8%20%D0%B4%D0%BE%D1%81%D1%82%D0%B0%D1%82%D0%BE%D1%87%D0%BD%D0%BE%2C%20%D0%BF%D0%BE%D0%BB%D0%BE%D0%B6%D0%B8%D1%82%D1%8C%20%D0%B2%20%D1%80%D1%8E%D0%BA%D0%B7%D0%B0%D0%BA%20%D1%82%D0%B5%D0%BA%D1%83%D1%89%D0%B8%D0%B9%20%D0%BF%D1%80%D0%B5%D0%B4%D0%BC%D0%B5%D1%82%20%D1%86%D0%B5%D0%BB%D0%B8%D0%BA%D0%BE%D0%BC%0A%20%20%20%20%20%20%20%20%20%20%20%20res%20%2B%3D%20item.v%0A%20%20%20%20%20%20%20%20%20%20%20%20cap%20-%3D%20item.w%0A%20%20%20%20%20%20%20%20else%3A%0A%20%20%20%20%20%20%20%20%20%20%20%20%23%20%D0%95%D1%81%D0%BB%D0%B8%20%D0%BE%D1%81%D1%82%D0%B0%D0%B2%D1%88%D0%B5%D0%B9%D1%81%D1%8F%20%D0%B2%D0%BC%D0%B5%D1%81%D1%82%D0%B8%D0%BC%D0%BE%D1%81%D1%82%D0%B8%20%D0%BD%D0%B5%D0%B4%D0%BE%D1%81%D1%82%D0%B0%D1%82%D0%BE%D1%87%D0%BD%D0%BE%2C%20%D0%BF%D0%BE%D0%BB%D0%BE%D0%B6%D0%B8%D1%82%D1%8C%20%D0%B2%20%D1%80%D1%8E%D0%BA%D0%B7%D0%B0%D0%BA%20%D1%87%D0%B0%D1%81%D1%82%D1%8C%20%D1%82%D0%B5%D0%BA%D1%83%D1%89%D0%B5%D0%B3%D0%BE%20%D0%BF%D1%80%D0%B5%D0%B4%D0%BC%D0%B5%D1%82%D0%B0%0A%20%20%20%20%20%20%20%20%20%20%20%20res%20%2B%3D%20%28item.v%20%2F%20item.w%29%20%2A%20cap%0A%20%20%20%20%20%20%20%20%20%20%20%20%23%20%D0%A1%D0%B2%D0%BE%D0%B1%D0%BE%D0%B4%D0%BD%D0%BE%D0%B9%20%D0%B2%D0%BC%D0%B5%D1%81%D1%82%D0%B8%D0%BC%D0%BE%D1%81%D1%82%D0%B8%20%D0%B1%D0%BE%D0%BB%D1%8C%D1%88%D0%B5%20%D0%BD%D0%B5%20%D0%BE%D1%81%D1%82%D0%B0%D0%BB%D0%BE%D1%81%D1%8C%2C%20%D0%BF%D0%BE%D1%8D%D1%82%D0%BE%D0%BC%D1%83%20%D0%B2%D1%8B%D0%B9%D1%82%D0%B8%20%D0%B8%D0%B7%20%D1%86%D0%B8%D0%BA%D0%BB%D0%B0%0A%20%20%20%20%20%20%20%20%20%20%20%20break%0A%20%20%20%20return%20res%0A%0A%22%22%22Driver%20Code%22%22%22%0Aif%20__name__%20%3D%3D%20%22__main__%22%3A%0A%20%20%20%20wgt%20%3D%20%5B10%2C%2020%2C%2030%2C%2040%2C%2050%5D%0A%20%20%20%20val%20%3D%20%5B50%2C%20120%2C%20150%2C%20210%2C%20240%5D%0A%20%20%20%20cap%20%3D%2050%0A%20%20%20%20n%20%3D%20len%28wgt%29%0A%0A%20%20%20%20%23%20%D0%96%D0%B0%D0%B4%D0%BD%D1%8B%D0%B9%20%D0%B0%D0%BB%D0%B3%D0%BE%D1%80%D0%B8%D1%82%D0%BC%0A%20%20%20%20res%20%3D%20fractional_knapsack%28wgt%2C%20val%2C%20cap%29%0A%20%20%20%20print%28f%22%D0%9C%D0%B0%D0%BA%D1%81%D0%B8%D0%BC%D0%B0%D0%BB%D1%8C%D0%BD%D0%B0%D1%8F%20%D1%81%D1%82%D0%BE%D0%B8%D0%BC%D0%BE%D1%81%D1%82%D1%8C%20%D0%BF%D1%80%D0%B5%D0%B4%D0%BC%D0%B5%D1%82%D0%BE%D0%B2%20%D0%B1%D0%B5%D0%B7%20%D0%BF%D1%80%D0%B5%D0%B2%D1%8B%D1%88%D0%B5%D0%BD%D0%B8%D1%8F%20%D0%B2%D0%BC%D0%B5%D1%81%D1%82%D0%B8%D0%BC%D0%BE%D1%81%D1%82%D0%B8%20%D1%80%D1%8E%D0%BA%D0%B7%D0%B0%D0%BA%D0%B0%20%3D%20%7Bres%7D%22%29&codeDivHeight=472&codeDivWidth=350&cumulative=false&curInstr=8&heapPrimitives=nevernest&origin=opt-frontend.js&py=311&rawInputLstJSON=%5B%5D&textReferences=false"> </iframe></div>
|
||||
<div style="margin-top: 5px;"><a href="https://pythontutor.com/iframe-embed.html#code=class%20Item%3A%0A%20%20%20%20%22%22%22%D0%9F%D1%80%D0%B5%D0%B4%D0%BC%D0%B5%D1%82%22%22%22%0A%20%20%20%20def%20__init__%28self%2C%20w%3A%20int%2C%20v%3A%20int%29%3A%0A%20%20%20%20%20%20%20%20self.w%20%3D%20w%20%20%23%20%D0%92%D0%B5%D1%81%20%D0%BF%D1%80%D0%B5%D0%B4%D0%BC%D0%B5%D1%82%D0%B0%0A%20%20%20%20%20%20%20%20self.v%20%3D%20v%20%20%23%20%D0%A1%D1%82%D0%BE%D0%B8%D0%BC%D0%BE%D1%81%D1%82%D1%8C%20%D0%BF%D1%80%D0%B5%D0%B4%D0%BC%D0%B5%D1%82%D0%B0%0A%0Adef%20fractional_knapsack%28wgt%3A%20list%5Bint%5D%2C%20val%3A%20list%5Bint%5D%2C%20cap%3A%20int%29%20-%3E%20int%3A%0A%20%20%20%20%22%22%22%D0%94%D1%80%D0%BE%D0%B1%D0%BD%D1%8B%D0%B9%20%D1%80%D1%8E%D0%BA%D0%B7%D0%B0%D0%BA%3A%20%D0%B6%D0%B0%D0%B4%D0%BD%D1%8B%D0%B9%20%D0%B0%D0%BB%D0%B3%D0%BE%D1%80%D0%B8%D1%82%D0%BC%22%22%22%0A%20%20%20%20%23%20%D0%A1%D0%BE%D0%B7%D0%B4%D0%B0%D1%82%D1%8C%20%D1%81%D0%BF%D0%B8%D1%81%D0%BE%D0%BA%20%D0%BF%D1%80%D0%B5%D0%B4%D0%BC%D0%B5%D1%82%D0%BE%D0%B2%20%D1%81%20%D0%B4%D0%B2%D1%83%D0%BC%D1%8F%20%D1%81%D0%B2%D0%BE%D0%B9%D1%81%D1%82%D0%B2%D0%B0%D0%BC%D0%B8%3A%20%D0%B2%D0%B5%D1%81%20%D0%B8%20%D1%81%D1%82%D0%BE%D0%B8%D0%BC%D0%BE%D1%81%D1%82%D1%8C%0A%20%20%20%20items%20%3D%20%5BItem%28w%2C%20v%29%20for%20w%2C%20v%20in%20zip%28wgt%2C%20val%29%5D%0A%20%20%20%20%23%20%D0%9E%D1%82%D1%81%D0%BE%D1%80%D1%82%D0%B8%D1%80%D0%BE%D0%B2%D0%B0%D1%82%D1%8C%20%D0%BF%D0%BE%20%D1%83%D0%B4%D0%B5%D0%BB%D1%8C%D0%BD%D0%BE%D0%B9%20%D1%81%D1%82%D0%BE%D0%B8%D0%BC%D0%BE%D1%81%D1%82%D0%B8%20item.v%20%2F%20item.w%20%D0%B2%20%D0%BF%D0%BE%D1%80%D1%8F%D0%B4%D0%BA%D0%B5%20%D1%83%D0%B1%D1%8B%D0%B2%D0%B0%D0%BD%D0%B8%D1%8F%0A%20%20%20%20items.sort%28key%3Dlambda%20item%3A%20item.v%20%2F%20item.w%2C%20reverse%3DTrue%29%0A%20%20%20%20%23%20%D0%A6%D0%B8%D0%BA%D0%BB%D0%B8%D1%87%D0%B5%D1%81%D0%BA%D0%B8%D0%B9%20%D0%B6%D0%B0%D0%B4%D0%BD%D1%8B%D0%B9%20%D0%B2%D1%8B%D0%B1%D0%BE%D1%80%0A%20%20%20%20res%20%3D%200%0A%20%20%20%20for%20item%20in%20items%3A%0A%20%20%20%20%20%20%20%20if%20item.w%20%3C%3D%20cap%3A%0A%20%20%20%20%20%20%20%20%20%20%20%20%23%20%D0%95%D1%81%D0%BB%D0%B8%20%D0%BE%D1%81%D1%82%D0%B0%D0%B2%D1%88%D0%B5%D0%B9%D1%81%D1%8F%20%D0%B2%D0%BC%D0%B5%D1%81%D1%82%D0%B8%D0%BC%D0%BE%D1%81%D1%82%D0%B8%20%D0%B4%D0%BE%D1%81%D1%82%D0%B0%D1%82%D0%BE%D1%87%D0%BD%D0%BE%2C%20%D0%BF%D0%BE%D0%BB%D0%BE%D0%B6%D0%B8%D1%82%D1%8C%20%D0%B2%20%D1%80%D1%8E%D0%BA%D0%B7%D0%B0%D0%BA%20%D1%82%D0%B5%D0%BA%D1%83%D1%89%D0%B8%D0%B9%20%D0%BF%D1%80%D0%B5%D0%B4%D0%BC%D0%B5%D1%82%20%D1%86%D0%B5%D0%BB%D0%B8%D0%BA%D0%BE%D0%BC%0A%20%20%20%20%20%20%20%20%20%20%20%20res%20%2B%3D%20item.v%0A%20%20%20%20%20%20%20%20%20%20%20%20cap%20-%3D%20item.w%0A%20%20%20%20%20%20%20%20else%3A%0A%20%20%20%20%20%20%20%20%20%20%20%20%23%20%D0%95%D1%81%D0%BB%D0%B8%20%D0%BE%D1%81%D1%82%D0%B0%D0%B2%D1%88%D0%B5%D0%B9%D1%81%D1%8F%20%D0%B2%D0%BC%D0%B5%D1%81%D1%82%D0%B8%D0%BC%D0%BE%D1%81%D1%82%D0%B8%20%D0%BD%D0%B5%D0%B4%D0%BE%D1%81%D1%82%D0%B0%D1%82%D0%BE%D1%87%D0%BD%D0%BE%2C%20%D0%BF%D0%BE%D0%BB%D0%BE%D0%B6%D0%B8%D1%82%D1%8C%20%D0%B2%20%D1%80%D1%8E%D0%BA%D0%B7%D0%B0%D0%BA%20%D1%87%D0%B0%D1%81%D1%82%D1%8C%20%D1%82%D0%B5%D0%BA%D1%83%D1%89%D0%B5%D0%B3%D0%BE%20%D0%BF%D1%80%D0%B5%D0%B4%D0%BC%D0%B5%D1%82%D0%B0%0A%20%20%20%20%20%20%20%20%20%20%20%20res%20%2B%3D%20%28item.v%20%2F%20item.w%29%20%2A%20cap%0A%20%20%20%20%20%20%20%20%20%20%20%20%23%20%D0%A1%D0%B2%D0%BE%D0%B1%D0%BE%D0%B4%D0%BD%D0%BE%D0%B9%20%D0%B2%D0%BC%D0%B5%D1%81%D1%82%D0%B8%D0%BC%D0%BE%D1%81%D1%82%D0%B8%20%D0%B1%D0%BE%D0%BB%D1%8C%D1%88%D0%B5%20%D0%BD%D0%B5%20%D0%BE%D1%81%D1%82%D0%B0%D0%BB%D0%BE%D1%81%D1%8C%2C%20%D0%BF%D0%BE%D1%8D%D1%82%D0%BE%D0%BC%D1%83%20%D0%B2%D1%8B%D0%B9%D1%82%D0%B8%20%D0%B8%D0%B7%20%D1%86%D0%B8%D0%BA%D0%BB%D0%B0%0A%20%20%20%20%20%20%20%20%20%20%20%20break%0A%20%20%20%20return%20res%0A%0A%22%22%22Driver%20Code%22%22%22%0Aif%20__name__%20%3D%3D%20%22__main__%22%3A%0A%20%20%20%20wgt%20%3D%20%5B10%2C%2020%2C%2030%2C%2040%2C%2050%5D%0A%20%20%20%20val%20%3D%20%5B50%2C%20120%2C%20150%2C%20210%2C%20240%5D%0A%20%20%20%20cap%20%3D%2050%0A%20%20%20%20n%20%3D%20len%28wgt%29%0A%0A%20%20%20%20%23%20%D0%96%D0%B0%D0%B4%D0%BD%D1%8B%D0%B9%20%D0%B0%D0%BB%D0%B3%D0%BE%D1%80%D0%B8%D1%82%D0%BC%0A%20%20%20%20res%20%3D%20fractional_knapsack%28wgt%2C%20val%2C%20cap%29%0A%20%20%20%20print%28f%22%D0%9C%D0%B0%D0%BA%D1%81%D0%B8%D0%BC%D0%B0%D0%BB%D1%8C%D0%BD%D0%B0%D1%8F%20%D1%81%D1%82%D0%BE%D0%B8%D0%BC%D0%BE%D1%81%D1%82%D1%8C%20%D0%BF%D1%80%D0%B5%D0%B4%D0%BC%D0%B5%D1%82%D0%BE%D0%B2%20%D0%B1%D0%B5%D0%B7%20%D0%BF%D1%80%D0%B5%D0%B2%D1%8B%D1%88%D0%B5%D0%BD%D0%B8%D1%8F%20%D0%B2%D0%BC%D0%B5%D1%81%D1%82%D0%B8%D0%BC%D0%BE%D1%81%D1%82%D0%B8%20%D1%80%D1%8E%D0%BA%D0%B7%D0%B0%D0%BA%D0%B0%20%3D%20%7Bres%7D%22%29&codeDivHeight=800&codeDivWidth=600&cumulative=false&curInstr=8&heapPrimitives=nevernest&origin=opt-frontend.js&py=311&rawInputLstJSON=%5B%5D&textReferences=false" target="_blank" rel="noopener noreferrer">Во весь экран ></a></div>
|
||||
|
||||
Встроенный алгоритм сортировки обычно имеет временную сложность $O(\log n)$, а пространственная сложность обычно равна $O(\log n)$ или $O(n)$, в зависимости от конкретной реализации в языке программирования.
|
||||
|
||||
Помимо сортировки, в худшем случае потребуется пройти весь список предметов, **поэтому временная сложность равна $O(n)$**, где $n$ - число предметов.
|
||||
|
||||
Поскольку инициализируется список объектов `Item`, **пространственная сложность равна $O(n)$**.
|
||||
|
||||
### 3. Доказательство корректности
|
||||
|
||||
Используем доказательство от противного. Предположим, что предмет $x$ имеет наибольшую удельную ценность, некоторый алгоритм получил максимальную ценность `res`, но в найденном решении предмет $x$ отсутствует.
|
||||
|
||||
Теперь вынем из рюкзака произвольный предмет единичного веса и заменим его на предмет $x$ того же веса. Поскольку предмет $x$ имеет наибольшую удельную ценность, общая ценность после замены обязательно станет больше `res`. **Это противоречит тому, что `res` является оптимальным решением, а значит оптимальное решение обязательно содержит предмет $x$**.
|
||||
|
||||
Для других предметов в этом решении можно построить аналогичное противоречие. Иными словами, **предметы с большей удельной ценностью всегда являются более выгодным выбором**, а значит жадная стратегия корректна.
|
||||
|
||||
Как показано на рисунке 15-6, если рассматривать вес предметов и их удельную ценность как горизонтальную и вертикальную оси двумерной диаграммы, то задачу о дробном рюкзаке можно интерпретировать как «поиск максимальной площади, ограниченной конечным отрезком по горизонтали». Эта аналогия помогает понять корректность жадной стратегии с геометрической точки зрения.
|
||||
|
||||
{ class="animation-figure" }
|
||||
|
||||
<p align="center"> Рисунок 15-6 Геометрическая интерпретация задачи о дробном рюкзаке </p>
|
||||
@@ -0,0 +1,414 @@
|
||||
---
|
||||
comments: true
|
||||
---
|
||||
|
||||
# 15.1 Жадный алгоритм
|
||||
|
||||
<u>Жадный алгоритм (greedy algorithm)</u> - это распространенный подход к решению задач оптимизации. Его основная идея состоит в том, чтобы на каждом этапе принятия решения выбирать вариант, который выглядит наилучшим прямо сейчас, то есть жадно принимать локально оптимальные решения в надежде получить глобально оптимальный результат. Жадные алгоритмы лаконичны и эффективны, поэтому широко применяются во многих практических задачах.
|
||||
|
||||
Жадные алгоритмы и динамическое программирование часто используются для решения задач оптимизации. У них есть некоторое сходство, например оба опираются на свойство оптимальной подструктуры, но принципы работы различаются.
|
||||
|
||||
- Динамическое программирование учитывает все решения предыдущих этапов при выборе текущего решения и использует ответы для прошлых подзадач, чтобы построить ответ для текущей подзадачи.
|
||||
- Жадный алгоритм не учитывает прошлые решения, а просто движется вперед, каждый раз делая жадный выбор, постепенно сужая область задачи, пока она не будет решена.
|
||||
|
||||
Сначала разберем принцип работы жадного алгоритма на примере задачи «размен монет». Эта задача уже встречалась в разделе «задача о полном рюкзаке», поэтому она наверняка вам знакома.
|
||||
|
||||
!!! question
|
||||
|
||||
Дано $n$ видов монет. Номинал монеты $i$ равен $coins[i - 1]$, целевая сумма равна $amt$, причем каждую монету можно брать неограниченное число раз. Требуется найти минимальное число монет, которыми можно набрать целевую сумму. Если набрать сумму невозможно, верните $-1$.
|
||||
|
||||
Жадная стратегия для этой задачи показана на рисунке 15-1. Для заданной целевой суммы **мы жадно выбираем монету, которая не превышает ее и находится к ней ближе всего**, и повторяем этот шаг, пока не получим нужную сумму.
|
||||
|
||||
{ class="animation-figure" }
|
||||
|
||||
<p align="center"> Рисунок 15-1 Жадная стратегия для задачи о размене монет </p>
|
||||
|
||||
Код реализации выглядит следующим образом:
|
||||
|
||||
=== "Python"
|
||||
|
||||
```python title="coin_change_greedy.py"
|
||||
def coin_change_greedy(coins: list[int], amt: int) -> int:
|
||||
"""Размен монет: жадный алгоритм"""
|
||||
# Предположить, что список coins упорядочен
|
||||
i = len(coins) - 1
|
||||
count = 0
|
||||
# Циклически выполнять жадный выбор, пока не останется суммы
|
||||
while amt > 0:
|
||||
# Найти монету, которая меньше остатка суммы и наиболее к нему близка
|
||||
while i > 0 and coins[i] > amt:
|
||||
i -= 1
|
||||
# Выбрать coins[i]
|
||||
amt -= coins[i]
|
||||
count += 1
|
||||
# Если допустимое решение не найдено, вернуть -1
|
||||
return count if amt == 0 else -1
|
||||
```
|
||||
|
||||
=== "C++"
|
||||
|
||||
```cpp title="coin_change_greedy.cpp"
|
||||
/* Размен монет: жадный алгоритм */
|
||||
int coinChangeGreedy(vector<int> &coins, int amt) {
|
||||
// Предположить, что список coins упорядочен
|
||||
int i = coins.size() - 1;
|
||||
int count = 0;
|
||||
// Циклически выполнять жадный выбор, пока не останется суммы
|
||||
while (amt > 0) {
|
||||
// Найти монету, которая меньше остатка суммы и наиболее к нему близка
|
||||
while (i > 0 && coins[i] > amt) {
|
||||
i--;
|
||||
}
|
||||
// Выбрать coins[i]
|
||||
amt -= coins[i];
|
||||
count++;
|
||||
}
|
||||
// Если допустимое решение не найдено, вернуть -1
|
||||
return amt == 0 ? count : -1;
|
||||
}
|
||||
```
|
||||
|
||||
=== "Java"
|
||||
|
||||
```java title="coin_change_greedy.java"
|
||||
/* Размен монет: жадный алгоритм */
|
||||
int coinChangeGreedy(int[] coins, int amt) {
|
||||
// Предположить, что список coins упорядочен
|
||||
int i = coins.length - 1;
|
||||
int count = 0;
|
||||
// Циклически выполнять жадный выбор, пока не останется суммы
|
||||
while (amt > 0) {
|
||||
// Найти монету, которая меньше остатка суммы и наиболее к нему близка
|
||||
while (i > 0 && coins[i] > amt) {
|
||||
i--;
|
||||
}
|
||||
// Выбрать coins[i]
|
||||
amt -= coins[i];
|
||||
count++;
|
||||
}
|
||||
// Если допустимое решение не найдено, вернуть -1
|
||||
return amt == 0 ? count : -1;
|
||||
}
|
||||
```
|
||||
|
||||
=== "C#"
|
||||
|
||||
```csharp title="coin_change_greedy.cs"
|
||||
/* Размен монет: жадный алгоритм */
|
||||
int CoinChangeGreedy(int[] coins, int amt) {
|
||||
// Предположить, что список coins упорядочен
|
||||
int i = coins.Length - 1;
|
||||
int count = 0;
|
||||
// Циклически выполнять жадный выбор, пока не останется суммы
|
||||
while (amt > 0) {
|
||||
// Найти монету, которая меньше остатка суммы и наиболее к нему близка
|
||||
while (i > 0 && coins[i] > amt) {
|
||||
i--;
|
||||
}
|
||||
// Выбрать coins[i]
|
||||
amt -= coins[i];
|
||||
count++;
|
||||
}
|
||||
// Если допустимое решение не найдено, вернуть -1
|
||||
return amt == 0 ? count : -1;
|
||||
}
|
||||
```
|
||||
|
||||
=== "Go"
|
||||
|
||||
```go title="coin_change_greedy.go"
|
||||
/* Размен монет: жадный алгоритм */
|
||||
func coinChangeGreedy(coins []int, amt int) int {
|
||||
// Предположить, что список coins упорядочен
|
||||
i := len(coins) - 1
|
||||
count := 0
|
||||
// Циклически выполнять жадный выбор, пока не останется суммы
|
||||
for amt > 0 {
|
||||
// Найти монету, которая меньше остатка суммы и наиболее к нему близка
|
||||
for i > 0 && coins[i] > amt {
|
||||
i--
|
||||
}
|
||||
// Выбрать coins[i]
|
||||
amt -= coins[i]
|
||||
count++
|
||||
}
|
||||
// Если допустимое решение не найдено, вернуть -1
|
||||
if amt != 0 {
|
||||
return -1
|
||||
}
|
||||
return count
|
||||
}
|
||||
```
|
||||
|
||||
=== "Swift"
|
||||
|
||||
```swift title="coin_change_greedy.swift"
|
||||
/* Размен монет: жадный алгоритм */
|
||||
func coinChangeGreedy(coins: [Int], amt: Int) -> Int {
|
||||
// Предположить, что список coins упорядочен
|
||||
var i = coins.count - 1
|
||||
var count = 0
|
||||
var amt = amt
|
||||
// Циклически выполнять жадный выбор, пока не останется суммы
|
||||
while amt > 0 {
|
||||
// Найти монету, которая меньше остатка суммы и наиболее к нему близка
|
||||
while i > 0 && coins[i] > amt {
|
||||
i -= 1
|
||||
}
|
||||
// Выбрать coins[i]
|
||||
amt -= coins[i]
|
||||
count += 1
|
||||
}
|
||||
// Если допустимое решение не найдено, вернуть -1
|
||||
return amt == 0 ? count : -1
|
||||
}
|
||||
```
|
||||
|
||||
=== "JS"
|
||||
|
||||
```javascript title="coin_change_greedy.js"
|
||||
/* Размен монет: жадный алгоритм */
|
||||
function coinChangeGreedy(coins, amt) {
|
||||
// Предположить, что массив coins упорядочен
|
||||
let i = coins.length - 1;
|
||||
let count = 0;
|
||||
// Циклически выполнять жадный выбор, пока не останется суммы
|
||||
while (amt > 0) {
|
||||
// Найти монету, которая меньше остатка суммы и наиболее к нему близка
|
||||
while (i > 0 && coins[i] > amt) {
|
||||
i--;
|
||||
}
|
||||
// Выбрать coins[i]
|
||||
amt -= coins[i];
|
||||
count++;
|
||||
}
|
||||
// Если допустимое решение не найдено, вернуть -1
|
||||
return amt === 0 ? count : -1;
|
||||
}
|
||||
```
|
||||
|
||||
=== "TS"
|
||||
|
||||
```typescript title="coin_change_greedy.ts"
|
||||
/* Размен монет: жадный алгоритм */
|
||||
function coinChangeGreedy(coins: number[], amt: number): number {
|
||||
// Предположить, что массив coins упорядочен
|
||||
let i = coins.length - 1;
|
||||
let count = 0;
|
||||
// Циклически выполнять жадный выбор, пока не останется суммы
|
||||
while (amt > 0) {
|
||||
// Найти монету, которая меньше остатка суммы и наиболее к нему близка
|
||||
while (i > 0 && coins[i] > amt) {
|
||||
i--;
|
||||
}
|
||||
// Выбрать coins[i]
|
||||
amt -= coins[i];
|
||||
count++;
|
||||
}
|
||||
// Если допустимое решение не найдено, вернуть -1
|
||||
return amt === 0 ? count : -1;
|
||||
}
|
||||
```
|
||||
|
||||
=== "Dart"
|
||||
|
||||
```dart title="coin_change_greedy.dart"
|
||||
/* Размен монет: жадный алгоритм */
|
||||
int coinChangeGreedy(List<int> coins, int amt) {
|
||||
// Предположить, что список coins упорядочен
|
||||
int i = coins.length - 1;
|
||||
int count = 0;
|
||||
// Циклически выполнять жадный выбор, пока не останется суммы
|
||||
while (amt > 0) {
|
||||
// Найти монету, которая меньше остатка суммы и наиболее к нему близка
|
||||
while (i > 0 && coins[i] > amt) {
|
||||
i--;
|
||||
}
|
||||
// Выбрать coins[i]
|
||||
amt -= coins[i];
|
||||
count++;
|
||||
}
|
||||
// Если допустимое решение не найдено, вернуть -1
|
||||
return amt == 0 ? count : -1;
|
||||
}
|
||||
```
|
||||
|
||||
=== "Rust"
|
||||
|
||||
```rust title="coin_change_greedy.rs"
|
||||
/* Размен монет: жадный алгоритм */
|
||||
fn coin_change_greedy(coins: &[i32], mut amt: i32) -> i32 {
|
||||
// Предположить, что список coins упорядочен
|
||||
let mut i = coins.len() - 1;
|
||||
let mut count = 0;
|
||||
// Циклически выполнять жадный выбор, пока не останется суммы
|
||||
while amt > 0 {
|
||||
// Найти монету, которая меньше остатка суммы и наиболее к нему близка
|
||||
while i > 0 && coins[i] > amt {
|
||||
i -= 1;
|
||||
}
|
||||
// Выбрать coins[i]
|
||||
amt -= coins[i];
|
||||
count += 1;
|
||||
}
|
||||
// Если допустимое решение не найдено, вернуть -1
|
||||
if amt == 0 {
|
||||
count
|
||||
} else {
|
||||
-1
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
=== "C"
|
||||
|
||||
```c title="coin_change_greedy.c"
|
||||
/* Размен монет: жадный алгоритм */
|
||||
int coinChangeGreedy(int *coins, int size, int amt) {
|
||||
// Предположить, что список coins упорядочен
|
||||
int i = size - 1;
|
||||
int count = 0;
|
||||
// Циклически выполнять жадный выбор, пока не останется суммы
|
||||
while (amt > 0) {
|
||||
// Найти монету, которая меньше остатка суммы и наиболее к нему близка
|
||||
while (i > 0 && coins[i] > amt) {
|
||||
i--;
|
||||
}
|
||||
// Выбрать coins[i]
|
||||
amt -= coins[i];
|
||||
count++;
|
||||
}
|
||||
// Если допустимое решение не найдено, вернуть -1
|
||||
return amt == 0 ? count : -1;
|
||||
}
|
||||
```
|
||||
|
||||
=== "Kotlin"
|
||||
|
||||
```kotlin title="coin_change_greedy.kt"
|
||||
/* Размен монет: жадный алгоритм */
|
||||
fun coinChangeGreedy(coins: IntArray, amt: Int): Int {
|
||||
// Предположить, что список coins упорядочен
|
||||
var am = amt
|
||||
var i = coins.size - 1
|
||||
var count = 0
|
||||
// Циклически выполнять жадный выбор, пока не останется суммы
|
||||
while (am > 0) {
|
||||
// Найти монету, которая меньше остатка суммы и наиболее к нему близка
|
||||
while (i > 0 && coins[i] > am) {
|
||||
i--
|
||||
}
|
||||
// Выбрать coins[i]
|
||||
am -= coins[i]
|
||||
count++
|
||||
}
|
||||
// Если допустимое решение не найдено, вернуть -1
|
||||
return if (am == 0) count else -1
|
||||
}
|
||||
```
|
||||
|
||||
=== "Ruby"
|
||||
|
||||
```ruby title="coin_change_greedy.rb"
|
||||
=begin
|
||||
File: coin_change_greedy.rb
|
||||
Created Time: 2024-05-07
|
||||
Author: Xuan Khoa Tu Nguyen (ngxktuzkai2000@gmail.com)
|
||||
=end
|
||||
|
||||
# ## Размен монет: жадный алгоритм ###
|
||||
def coin_change_greedy(coins, amt)
|
||||
# Предположить, что список coins упорядочен
|
||||
i = coins.length - 1
|
||||
count = 0
|
||||
# Циклически выполнять жадный выбор, пока не останется суммы
|
||||
while amt > 0
|
||||
# Найти монету, которая меньше остатка суммы и наиболее к нему близка
|
||||
while i > 0 && coins[i] > amt
|
||||
i -= 1
|
||||
end
|
||||
# Выбрать coins[i]
|
||||
amt -= coins[i]
|
||||
count += 1
|
||||
end
|
||||
# Если допустимое решение не найдено, вернуть -1
|
||||
amt == 0 ? count : -1
|
||||
end
|
||||
```
|
||||
|
||||
??? pythontutor "Визуализация кода"
|
||||
|
||||
<div style="height: 549px; width: 100%;"><iframe class="pythontutor-iframe" src="https://pythontutor.com/iframe-embed.html#code=def%20coin_change_greedy%28coins%3A%20list%5Bint%5D%2C%20amt%3A%20int%29%20-%3E%20int%3A%0A%20%20%20%20%22%22%22%D0%A0%D0%B0%D0%B7%D0%BC%D0%B5%D0%BD%20%D0%BC%D0%BE%D0%BD%D0%B5%D1%82%3A%20%D0%B6%D0%B0%D0%B4%D0%BD%D1%8B%D0%B9%20%D0%B0%D0%BB%D0%B3%D0%BE%D1%80%D0%B8%D1%82%D0%BC%22%22%22%0A%20%20%20%20%23%20%D0%9F%D1%80%D0%B5%D0%B4%D0%BF%D0%BE%D0%BB%D0%BE%D0%B6%D0%B8%D1%82%D1%8C%2C%20%D1%87%D1%82%D0%BE%20%D1%81%D0%BF%D0%B8%D1%81%D0%BE%D0%BA%20coins%20%D1%83%D0%BF%D0%BE%D1%80%D1%8F%D0%B4%D0%BE%D1%87%D0%B5%D0%BD%0A%20%20%20%20i%20%3D%20len%28coins%29%20-%201%0A%20%20%20%20count%20%3D%200%0A%20%20%20%20%23%20%D0%A6%D0%B8%D0%BA%D0%BB%D0%B8%D1%87%D0%B5%D1%81%D0%BA%D0%B8%20%D0%B2%D1%8B%D0%BF%D0%BE%D0%BB%D0%BD%D1%8F%D1%82%D1%8C%20%D0%B6%D0%B0%D0%B4%D0%BD%D1%8B%D0%B9%20%D0%B2%D1%8B%D0%B1%D0%BE%D1%80%2C%20%D0%BF%D0%BE%D0%BA%D0%B0%20%D0%BD%D0%B5%20%D0%BE%D1%81%D1%82%D0%B0%D0%BD%D0%B5%D1%82%D1%81%D1%8F%20%D1%81%D1%83%D0%BC%D0%BC%D1%8B%0A%20%20%20%20while%20amt%20%3E%200%3A%0A%20%20%20%20%20%20%20%20%23%20%D0%9D%D0%B0%D0%B9%D1%82%D0%B8%20%D0%BC%D0%BE%D0%BD%D0%B5%D1%82%D1%83%2C%20%D0%BA%D0%BE%D1%82%D0%BE%D1%80%D0%B0%D1%8F%20%D0%BC%D0%B5%D0%BD%D1%8C%D1%88%D0%B5%20%D0%BE%D1%81%D1%82%D0%B0%D1%82%D0%BA%D0%B0%20%D1%81%D1%83%D0%BC%D0%BC%D1%8B%20%D0%B8%20%D0%BD%D0%B0%D0%B8%D0%B1%D0%BE%D0%BB%D0%B5%D0%B5%20%D0%BA%20%D0%BD%D0%B5%D0%BC%D1%83%20%D0%B1%D0%BB%D0%B8%D0%B7%D0%BA%D0%B0%0A%20%20%20%20%20%20%20%20while%20i%20%3E%200%20and%20coins%5Bi%5D%20%3E%20amt%3A%0A%20%20%20%20%20%20%20%20%20%20%20%20i%20-%3D%201%0A%20%20%20%20%20%20%20%20%23%20%D0%92%D1%8B%D0%B1%D1%80%D0%B0%D1%82%D1%8C%20coins%5Bi%5D%0A%20%20%20%20%20%20%20%20amt%20-%3D%20coins%5Bi%5D%0A%20%20%20%20%20%20%20%20count%20%2B%3D%201%0A%20%20%20%20%23%20%D0%95%D1%81%D0%BB%D0%B8%20%D0%B4%D0%BE%D0%BF%D1%83%D1%81%D1%82%D0%B8%D0%BC%D0%BE%D0%B5%20%D1%80%D0%B5%D1%88%D0%B5%D0%BD%D0%B8%D0%B5%20%D0%BD%D0%B5%20%D0%BD%D0%B0%D0%B9%D0%B4%D0%B5%D0%BD%D0%BE%2C%20%D0%B2%D0%B5%D1%80%D0%BD%D1%83%D1%82%D1%8C%20-1%0A%20%20%20%20return%20count%20if%20amt%20%3D%3D%200%20else%20-1%0A%0A%0A%22%22%22Driver%20Code%22%22%22%0Aif%20__name__%20%3D%3D%20%22__main__%22%3A%0A%20%20%20%20%23%20%D0%96%D0%B0%D0%B4%D0%BD%D1%8B%D0%B9%20%D0%BF%D0%BE%D0%B4%D1%85%D0%BE%D0%B4%3A%20%D0%B3%D0%B0%D1%80%D0%B0%D0%BD%D1%82%D0%B8%D1%80%D1%83%D0%B5%D1%82%20%D0%BD%D0%B0%D1%85%D0%BE%D0%B6%D0%B4%D0%B5%D0%BD%D0%B8%D0%B5%20%D0%B3%D0%BB%D0%BE%D0%B1%D0%B0%D0%BB%D1%8C%D0%BD%D0%BE%20%D0%BE%D0%BF%D1%82%D0%B8%D0%BC%D0%B0%D0%BB%D1%8C%D0%BD%D0%BE%D0%B3%D0%BE%20%D1%80%D0%B5%D1%88%D0%B5%D0%BD%D0%B8%D1%8F%0A%20%20%20%20coins%20%3D%20%5B1%2C%205%2C%2010%2C%2020%2C%2050%2C%20100%5D%0A%20%20%20%20amt%20%3D%20186%0A%20%20%20%20res%20%3D%20coin_change_greedy%28coins%2C%20amt%29%0A%20%20%20%20print%28f%22%5Cncoins%20%3D%20%7Bcoins%7D%2C%20amt%20%3D%20%7Bamt%7D%22%29%0A%20%20%20%20print%28f%22%D0%9C%D0%B8%D0%BD%D0%B8%D0%BC%D0%B0%D0%BB%D1%8C%D0%BD%D0%BE%D0%B5%20%D1%87%D0%B8%D1%81%D0%BB%D0%BE%20%D0%BC%D0%BE%D0%BD%D0%B5%D1%82%20%D0%B4%D0%BB%D1%8F%20%D0%BD%D0%B0%D0%B1%D0%BE%D1%80%D0%B0%20%D1%81%D1%83%D0%BC%D0%BC%D1%8B%20%7Bamt%7D%20%3D%20%7Bres%7D%22%29%0A%0A%20%20%20%20%23%20%D0%96%D0%B0%D0%B4%D0%BD%D1%8B%D0%B9%20%D0%BF%D0%BE%D0%B4%D1%85%D0%BE%D0%B4%3A%20%D0%BD%D0%B5%20%D0%B3%D0%B0%D1%80%D0%B0%D0%BD%D1%82%D0%B8%D1%80%D1%83%D0%B5%D1%82%20%D0%BD%D0%B0%D1%85%D0%BE%D0%B6%D0%B4%D0%B5%D0%BD%D0%B8%D0%B5%20%D0%B3%D0%BB%D0%BE%D0%B1%D0%B0%D0%BB%D1%8C%D0%BD%D0%BE%20%D0%BE%D0%BF%D1%82%D0%B8%D0%BC%D0%B0%D0%BB%D1%8C%D0%BD%D0%BE%D0%B3%D0%BE%20%D1%80%D0%B5%D1%88%D0%B5%D0%BD%D0%B8%D1%8F%0A%20%20%20%20coins%20%3D%20%5B1%2C%2020%2C%2050%5D%0A%20%20%20%20amt%20%3D%2060%0A%20%20%20%20res%20%3D%20coin_change_greedy%28coins%2C%20amt%29%0A%20%20%20%20print%28f%22%5Cncoins%20%3D%20%7Bcoins%7D%2C%20amt%20%3D%20%7Bamt%7D%22%29%0A%20%20%20%20print%28f%22%D0%9C%D0%B8%D0%BD%D0%B8%D0%BC%D0%B0%D0%BB%D1%8C%D0%BD%D0%BE%D0%B5%20%D1%87%D0%B8%D1%81%D0%BB%D0%BE%20%D0%BC%D0%BE%D0%BD%D0%B5%D1%82%20%D0%B4%D0%BB%D1%8F%20%D0%BD%D0%B0%D0%B1%D0%BE%D1%80%D0%B0%20%D1%81%D1%83%D0%BC%D0%BC%D1%8B%20%7Bamt%7D%20%3D%20%7Bres%7D%22%29%0A%20%20%20%20print%28f%22%D0%9D%D0%B0%20%D1%81%D0%B0%D0%BC%D0%BE%D0%BC%20%D0%B4%D0%B5%D0%BB%D0%B5%20%D0%BC%D0%B8%D0%BD%D0%B8%D0%BC%D1%83%D0%BC%20%D1%80%D0%B0%D0%B2%D0%B5%D0%BD%203%3A%2020%20%2B%2020%20%2B%2020%22%29&codeDivHeight=472&codeDivWidth=350&cumulative=false&curInstr=5&heapPrimitives=nevernest&origin=opt-frontend.js&py=311&rawInputLstJSON=%5B%5D&textReferences=false"> </iframe></div>
|
||||
<div style="margin-top: 5px;"><a href="https://pythontutor.com/iframe-embed.html#code=def%20coin_change_greedy%28coins%3A%20list%5Bint%5D%2C%20amt%3A%20int%29%20-%3E%20int%3A%0A%20%20%20%20%22%22%22%D0%A0%D0%B0%D0%B7%D0%BC%D0%B5%D0%BD%20%D0%BC%D0%BE%D0%BD%D0%B5%D1%82%3A%20%D0%B6%D0%B0%D0%B4%D0%BD%D1%8B%D0%B9%20%D0%B0%D0%BB%D0%B3%D0%BE%D1%80%D0%B8%D1%82%D0%BC%22%22%22%0A%20%20%20%20%23%20%D0%9F%D1%80%D0%B5%D0%B4%D0%BF%D0%BE%D0%BB%D0%BE%D0%B6%D0%B8%D1%82%D1%8C%2C%20%D1%87%D1%82%D0%BE%20%D1%81%D0%BF%D0%B8%D1%81%D0%BE%D0%BA%20coins%20%D1%83%D0%BF%D0%BE%D1%80%D1%8F%D0%B4%D0%BE%D1%87%D0%B5%D0%BD%0A%20%20%20%20i%20%3D%20len%28coins%29%20-%201%0A%20%20%20%20count%20%3D%200%0A%20%20%20%20%23%20%D0%A6%D0%B8%D0%BA%D0%BB%D0%B8%D1%87%D0%B5%D1%81%D0%BA%D0%B8%20%D0%B2%D1%8B%D0%BF%D0%BE%D0%BB%D0%BD%D1%8F%D1%82%D1%8C%20%D0%B6%D0%B0%D0%B4%D0%BD%D1%8B%D0%B9%20%D0%B2%D1%8B%D0%B1%D0%BE%D1%80%2C%20%D0%BF%D0%BE%D0%BA%D0%B0%20%D0%BD%D0%B5%20%D0%BE%D1%81%D1%82%D0%B0%D0%BD%D0%B5%D1%82%D1%81%D1%8F%20%D1%81%D1%83%D0%BC%D0%BC%D1%8B%0A%20%20%20%20while%20amt%20%3E%200%3A%0A%20%20%20%20%20%20%20%20%23%20%D0%9D%D0%B0%D0%B9%D1%82%D0%B8%20%D0%BC%D0%BE%D0%BD%D0%B5%D1%82%D1%83%2C%20%D0%BA%D0%BE%D1%82%D0%BE%D1%80%D0%B0%D1%8F%20%D0%BC%D0%B5%D0%BD%D1%8C%D1%88%D0%B5%20%D0%BE%D1%81%D1%82%D0%B0%D1%82%D0%BA%D0%B0%20%D1%81%D1%83%D0%BC%D0%BC%D1%8B%20%D0%B8%20%D0%BD%D0%B0%D0%B8%D0%B1%D0%BE%D0%BB%D0%B5%D0%B5%20%D0%BA%20%D0%BD%D0%B5%D0%BC%D1%83%20%D0%B1%D0%BB%D0%B8%D0%B7%D0%BA%D0%B0%0A%20%20%20%20%20%20%20%20while%20i%20%3E%200%20and%20coins%5Bi%5D%20%3E%20amt%3A%0A%20%20%20%20%20%20%20%20%20%20%20%20i%20-%3D%201%0A%20%20%20%20%20%20%20%20%23%20%D0%92%D1%8B%D0%B1%D1%80%D0%B0%D1%82%D1%8C%20coins%5Bi%5D%0A%20%20%20%20%20%20%20%20amt%20-%3D%20coins%5Bi%5D%0A%20%20%20%20%20%20%20%20count%20%2B%3D%201%0A%20%20%20%20%23%20%D0%95%D1%81%D0%BB%D0%B8%20%D0%B4%D0%BE%D0%BF%D1%83%D1%81%D1%82%D0%B8%D0%BC%D0%BE%D0%B5%20%D1%80%D0%B5%D1%88%D0%B5%D0%BD%D0%B8%D0%B5%20%D0%BD%D0%B5%20%D0%BD%D0%B0%D0%B9%D0%B4%D0%B5%D0%BD%D0%BE%2C%20%D0%B2%D0%B5%D1%80%D0%BD%D1%83%D1%82%D1%8C%20-1%0A%20%20%20%20return%20count%20if%20amt%20%3D%3D%200%20else%20-1%0A%0A%0A%22%22%22Driver%20Code%22%22%22%0Aif%20__name__%20%3D%3D%20%22__main__%22%3A%0A%20%20%20%20%23%20%D0%96%D0%B0%D0%B4%D0%BD%D1%8B%D0%B9%20%D0%BF%D0%BE%D0%B4%D1%85%D0%BE%D0%B4%3A%20%D0%B3%D0%B0%D1%80%D0%B0%D0%BD%D1%82%D0%B8%D1%80%D1%83%D0%B5%D1%82%20%D0%BD%D0%B0%D1%85%D0%BE%D0%B6%D0%B4%D0%B5%D0%BD%D0%B8%D0%B5%20%D0%B3%D0%BB%D0%BE%D0%B1%D0%B0%D0%BB%D1%8C%D0%BD%D0%BE%20%D0%BE%D0%BF%D1%82%D0%B8%D0%BC%D0%B0%D0%BB%D1%8C%D0%BD%D0%BE%D0%B3%D0%BE%20%D1%80%D0%B5%D1%88%D0%B5%D0%BD%D0%B8%D1%8F%0A%20%20%20%20coins%20%3D%20%5B1%2C%205%2C%2010%2C%2020%2C%2050%2C%20100%5D%0A%20%20%20%20amt%20%3D%20186%0A%20%20%20%20res%20%3D%20coin_change_greedy%28coins%2C%20amt%29%0A%20%20%20%20print%28f%22%5Cncoins%20%3D%20%7Bcoins%7D%2C%20amt%20%3D%20%7Bamt%7D%22%29%0A%20%20%20%20print%28f%22%D0%9C%D0%B8%D0%BD%D0%B8%D0%BC%D0%B0%D0%BB%D1%8C%D0%BD%D0%BE%D0%B5%20%D1%87%D0%B8%D1%81%D0%BB%D0%BE%20%D0%BC%D0%BE%D0%BD%D0%B5%D1%82%20%D0%B4%D0%BB%D1%8F%20%D0%BD%D0%B0%D0%B1%D0%BE%D1%80%D0%B0%20%D1%81%D1%83%D0%BC%D0%BC%D1%8B%20%7Bamt%7D%20%3D%20%7Bres%7D%22%29%0A%0A%20%20%20%20%23%20%D0%96%D0%B0%D0%B4%D0%BD%D1%8B%D0%B9%20%D0%BF%D0%BE%D0%B4%D1%85%D0%BE%D0%B4%3A%20%D0%BD%D0%B5%20%D0%B3%D0%B0%D1%80%D0%B0%D0%BD%D1%82%D0%B8%D1%80%D1%83%D0%B5%D1%82%20%D0%BD%D0%B0%D1%85%D0%BE%D0%B6%D0%B4%D0%B5%D0%BD%D0%B8%D0%B5%20%D0%B3%D0%BB%D0%BE%D0%B1%D0%B0%D0%BB%D1%8C%D0%BD%D0%BE%20%D0%BE%D0%BF%D1%82%D0%B8%D0%BC%D0%B0%D0%BB%D1%8C%D0%BD%D0%BE%D0%B3%D0%BE%20%D1%80%D0%B5%D1%88%D0%B5%D0%BD%D0%B8%D1%8F%0A%20%20%20%20coins%20%3D%20%5B1%2C%2020%2C%2050%5D%0A%20%20%20%20amt%20%3D%2060%0A%20%20%20%20res%20%3D%20coin_change_greedy%28coins%2C%20amt%29%0A%20%20%20%20print%28f%22%5Cncoins%20%3D%20%7Bcoins%7D%2C%20amt%20%3D%20%7Bamt%7D%22%29%0A%20%20%20%20print%28f%22%D0%9C%D0%B8%D0%BD%D0%B8%D0%BC%D0%B0%D0%BB%D1%8C%D0%BD%D0%BE%D0%B5%20%D1%87%D0%B8%D1%81%D0%BB%D0%BE%20%D0%BC%D0%BE%D0%BD%D0%B5%D1%82%20%D0%B4%D0%BB%D1%8F%20%D0%BD%D0%B0%D0%B1%D0%BE%D1%80%D0%B0%20%D1%81%D1%83%D0%BC%D0%BC%D1%8B%20%7Bamt%7D%20%3D%20%7Bres%7D%22%29%0A%20%20%20%20print%28f%22%D0%9D%D0%B0%20%D1%81%D0%B0%D0%BC%D0%BE%D0%BC%20%D0%B4%D0%B5%D0%BB%D0%B5%20%D0%BC%D0%B8%D0%BD%D0%B8%D0%BC%D1%83%D0%BC%20%D1%80%D0%B0%D0%B2%D0%B5%D0%BD%203%3A%2020%20%2B%2020%20%2B%2020%22%29&codeDivHeight=800&codeDivWidth=600&cumulative=false&curInstr=5&heapPrimitives=nevernest&origin=opt-frontend.js&py=311&rawInputLstJSON=%5B%5D&textReferences=false" target="_blank" rel="noopener noreferrer">Во весь экран ></a></div>
|
||||
|
||||
У вас может невольно вырваться: So clean! Жадный алгоритм решает задачу размена монет всего примерно десятью строками кода.
|
||||
|
||||
## 15.1.1 Преимущества и ограничения жадного алгоритма
|
||||
|
||||
**Жадный алгоритм не только прост в действиях и реализации, но и обычно очень эффективен**. В приведенном выше коде обозначим минимальный номинал монеты через $\min(coins)$, тогда жадный выбор выполняется не более чем $amt / \min(coins)$ раз, а временная сложность равна $O(amt / \min(coins))$. Это на порядок меньше, чем временная сложность решения через динамическое программирование $O(n \times amt)$.
|
||||
|
||||
Однако **для некоторых наборов номиналов монет жадный алгоритм не может найти оптимальный ответ**. Ниже показаны два примера.
|
||||
|
||||
- **Положительный пример $coins = [1, 5, 10, 20, 50, 100]$**: для такого набора монет при любом $amt$ жадный алгоритм находит оптимальное решение.
|
||||
- **Отрицательный пример $coins = [1, 20, 50]$**: пусть $amt = 60$. Жадный алгоритм найдет только комбинацию $50 + 1 \times 10$, то есть всего $11$ монет, тогда как динамическое программирование находит оптимум $20 + 20 + 20$, где требуется лишь $3$ монеты.
|
||||
- **Отрицательный пример $coins = [1, 49, 50]$**: пусть $amt = 98$. Жадный алгоритм найдет только комбинацию $50 + 1 \times 48$, то есть всего $49$ монет, тогда как динамическое программирование находит оптимум $49 + 49$, где требуется лишь $2$ монеты.
|
||||
|
||||
{ class="animation-figure" }
|
||||
|
||||
<p align="center"> Рисунок 15-2 Примеры, где жадный алгоритм не находит оптимального решения </p>
|
||||
|
||||
Иными словами, в задаче о размене монет жадный алгоритм не гарантирует нахождение глобально оптимального решения и иногда может приводить к очень плохому ответу. Для этой задачи больше подходит динамическое программирование.
|
||||
|
||||
В общем случае жадный алгоритм применим в двух следующих ситуациях.
|
||||
|
||||
1. **Можно гарантировать нахождение оптимального решения**: в таком случае жадный алгоритм часто является лучшим выбором, поскольку обычно он эффективнее, чем поиск с возвратом и динамическое программирование.
|
||||
2. **Можно найти приближенно оптимальное решение**: в таком случае жадный алгоритм тоже полезен. Для многих сложных задач поиск глобального оптимума очень труден, и возможность быстро найти субоптимальный ответ уже весьма ценна.
|
||||
|
||||
## 15.1.2 Свойства жадного алгоритма
|
||||
|
||||
Тогда возникает вопрос: какие задачи подходят для решения жадным алгоритмом? Или, другими словами, в каких случаях жадный алгоритм может гарантировать оптимальный ответ?
|
||||
|
||||
По сравнению с динамическим программированием условия применения жадного алгоритма строже. В основном нас интересуют два свойства задачи.
|
||||
|
||||
- **Свойство жадного выбора**: только когда локально оптимальный выбор всегда может привести к глобально оптимальному решению, жадный алгоритм способен гарантировать оптимум.
|
||||
- **Оптимальная подструктура**: оптимальное решение исходной задачи содержит оптимальные решения подзадач.
|
||||
|
||||
Оптимальная подструктура уже обсуждалась в главе «Динамическое программирование», поэтому здесь не будем повторяться. Стоит отметить, что у некоторых задач оптимальная подструктура не столь очевидна, но их все равно можно решать жадным алгоритмом.
|
||||
|
||||
Основное внимание мы уделяем тому, как определить свойство жадного выбора. Хотя формулировка выглядит довольно простой, **на практике для многих задач доказать свойство жадного выбора совсем не легко**.
|
||||
|
||||
Например, в задаче о размене монет легко привести контрпример и опровергнуть свойство жадного выбора, но вот доказать его истинность намного сложнее. Если спросить: **для каких наборов монет можно использовать жадный алгоритм**? - обычно удается дать лишь интуитивный или примерный ответ, а не строгое математическое доказательство.
|
||||
|
||||
!!! quote
|
||||
|
||||
Существует статья, в которой приводится алгоритм со временной сложностью $O(n^3)$ для определения того, можно ли с помощью жадного алгоритма находить оптимальный размен для любой суммы в заданной системе монет.
|
||||
|
||||
Pearson, D. A polynomial-time algorithm for the change-making problem[J]. Operations Research Letters, 2005, 33(3): 231-234.
|
||||
|
||||
## 15.1.3 Этапы решения задач жадным алгоритмом
|
||||
|
||||
В общем виде процесс решения жадной задачи можно разбить на три шага.
|
||||
|
||||
1. **Анализ задачи**: разобраться в свойствах задачи, включая определение состояний, целевой функции и ограничений. Этот этап присутствует и в поиске с возвратом, и в динамическом программировании.
|
||||
2. **Определение жадной стратегии**: определить, какой жадный выбор следует делать на каждом шаге. Эта стратегия должна уменьшать размер задачи на каждом этапе и в итоге привести к решению всей задачи.
|
||||
3. **Доказательство корректности**: обычно требуется доказать, что задача обладает свойством жадного выбора и оптимальной подструктурой. На этом этапе может понадобиться математическое доказательство, например индукция или доказательство от противного.
|
||||
|
||||
Определение жадной стратегии - это ключевой этап решения, но на практике он часто оказывается непростым по следующим причинам.
|
||||
|
||||
- **Жадные стратегии для разных задач сильно различаются**. Для многих задач стратегия довольно очевидна, и до нее можно дойти за счет общих рассуждений и нескольких проб. Но в более сложных задачах жадная стратегия может быть очень скрытой, и тут уже многое зависит от опыта решения задач и алгоритмической подготовки.
|
||||
- **Некоторые жадные стратегии выглядят убедительно, но оказываются обманчивыми**. Бывает, что мы с уверенностью придумали жадную стратегию, написали код и отправили его на проверку, а часть тестов не проходит. Причина в том, что спроектированная стратегия лишь «частично верна», и описанная выше задача о размене монет - типичный пример.
|
||||
|
||||
Чтобы гарантировать корректность, нужно дать строгое математическое доказательство жадной стратегии, **обычно с использованием доказательства от противного или математической индукции**.
|
||||
|
||||
Однако и доказательство корректности может оказаться непростой задачей. Если идей нет, мы обычно начинаем отлаживать код на тестовых примерах, постепенно меняя и проверяя жадную стратегию.
|
||||
|
||||
## 15.1.4 Типичные задачи для жадного алгоритма
|
||||
|
||||
Жадные алгоритмы часто применяются в задачах оптимизации, которые обладают свойством жадного выбора и оптимальной подструктурой. Ниже приведены некоторые типичные задачи, решаемые жадным подходом.
|
||||
|
||||
- **Задача о размене монет**: при некоторых системах монет жадный алгоритм всегда дает оптимальный ответ.
|
||||
- **Задача о расписании интервалов**: пусть есть несколько задач, каждая выполняется в некотором временном интервале, и требуется завершить как можно больше задач. Если каждый раз выбирать задачу с самым ранним временем окончания, то жадный алгоритм дает оптимальный ответ.
|
||||
- **Задача о дробном рюкзаке**: дана группа предметов и грузоподъемность. Требуется выбрать предметы так, чтобы их общий вес не превышал ограничение, а общая ценность была максимальной. Если каждый раз выбирать предмет с наилучшим отношением стоимости к весу, то в некоторых случаях жадный алгоритм дает оптимальный ответ.
|
||||
- **Задача о покупке и продаже акций**: дана история цен акции. Можно совершать несколько сделок, но если акция уже куплена, то до продажи покупать снова нельзя. Цель - получить максимальную прибыль.
|
||||
- **Код Хаффмана**: это жадный алгоритм для сжатия данных без потерь. Построив дерево Хаффмана и каждый раз объединяя два узла с наименьшей частотой, мы получаем дерево с минимальной взвешенной длиной пути, то есть минимальной длиной кодирования.
|
||||
- **Алгоритм Дейкстры**: это жадный алгоритм решения задачи о кратчайших путях от заданной исходной вершины до всех остальных вершин.
|
||||
@@ -0,0 +1,22 @@
|
||||
---
|
||||
comments: true
|
||||
icon: material/head-heart-outline
|
||||
---
|
||||
|
||||
# Глава 15. Жадность
|
||||
|
||||
{ class="cover-image" }
|
||||
|
||||
!!! abstract
|
||||
|
||||
Подсолнух поворачивается к солнцу, постоянно стремясь к наилучшим условиям для роста.
|
||||
|
||||
Жадная стратегия через цепочку простых выборов постепенно приводит к наилучшему ответу.
|
||||
|
||||
## Содержание главы
|
||||
|
||||
- [15.1 Жадный алгоритм](greedy_algorithm.md)
|
||||
- [15.2 Задача о дробном рюкзаке](fractional_knapsack_problem.md)
|
||||
- [15.3 Задача о максимальной вместимости](max_capacity_problem.md)
|
||||
- [15.4 Задача о максимальном произведении разбиения](max_product_cutting_problem.md)
|
||||
- [15.5 Резюме](summary.md)
|
||||
@@ -0,0 +1,451 @@
|
||||
---
|
||||
comments: true
|
||||
---
|
||||
|
||||
# 15.3 Задача о максимальной вместимости
|
||||
|
||||
!!! question
|
||||
|
||||
Дан массив $ht$, где каждый элемент обозначает высоту вертикальной перегородки. Любые две перегородки в массиве вместе с пространством между ними образуют контейнер.
|
||||
|
||||
Вместимость контейнера равна произведению высоты и ширины (площади), где высота определяется более короткой перегородкой, а ширина - разностью индексов двух перегородок в массиве.
|
||||
|
||||
Требуется выбрать две перегородки так, чтобы образованный ими контейнер имел максимальную вместимость. Пример показан на рисунке 15-7.
|
||||
|
||||
{ class="animation-figure" }
|
||||
|
||||
<p align="center"> Рисунок 15-7 Пример данных для задачи о максимальной вместимости </p>
|
||||
|
||||
Контейнер образуется произвольными двумя перегородками, **поэтому состоянием задачи служит пара индексов этих перегородок, обозначим ее как $[i, j]$**.
|
||||
|
||||
Согласно условию, вместимость равна произведению высоты на ширину, где высота определяется короткой перегородкой, а ширина - разностью индексов двух перегородок. Обозначим вместимость через $cap[i, j]$, тогда формула принимает вид:
|
||||
|
||||
$$
|
||||
cap[i, j] = \min(ht[i], ht[j]) \times (j - i)
|
||||
$$
|
||||
|
||||
Пусть длина массива равна $n$. Тогда число пар перегородок, то есть общее число состояний, равно $C_n^2 = \frac{n(n - 1)}{2}$. Самый прямолинейный подход - **перебрать все состояния**, после чего найти максимальную вместимость. Его временная сложность равна $O(n^2)$.
|
||||
|
||||
### 1. Определение жадной стратегии
|
||||
|
||||
У этой задачи есть и более эффективное решение. Как показано на рисунке 15-8, рассмотрим состояние $[i, j]$, где индексы удовлетворяют $i < j$, а высоты - условию $ht[i] < ht[j]$, то есть $i$ - короткая перегородка, а $j$ - длинная.
|
||||
|
||||
{ class="animation-figure" }
|
||||
|
||||
<p align="center"> Рисунок 15-8 Начальное состояние </p>
|
||||
|
||||
Как показано на рисунке 15-9, **если в этот момент сдвинуть длинную перегородку $j$ ближе к короткой перегородке $i$, то вместимость обязательно уменьшится**.
|
||||
|
||||
Причина в том, что после смещения длинной перегородки $j$ ширина $j-i$ обязательно станет меньше, а высота определяется короткой перегородкой, поэтому высота либо останется прежней (если $i$ останется короткой перегородкой), либо уменьшится (если сдвинутая $j$ станет короткой перегородкой).
|
||||
|
||||
{ class="animation-figure" }
|
||||
|
||||
<p align="center"> Рисунок 15-9 Состояние после перемещения длинной перегородки внутрь </p>
|
||||
|
||||
Рассуждая в обратную сторону, **только сдвигая короткую перегородку $i$ внутрь, мы можем получить шанс увеличить вместимость**. Хотя ширина при этом обязательно уменьшится, **высота может возрасти** (если после перемещения короткая перегородка $i$ станет выше). Например, на рисунке 15-10 после перемещения короткой перегородки площадь увеличивается.
|
||||
|
||||
{ class="animation-figure" }
|
||||
|
||||
<p align="center"> Рисунок 15-10 Состояние после перемещения короткой перегородки внутрь </p>
|
||||
|
||||
Отсюда и выводится жадная стратегия для этой задачи: инициализировать два указателя по краям контейнера и на каждом шаге сдвигать внутрь указатель, соответствующий короткой перегородке, пока указатели не встретятся.
|
||||
|
||||
На рисунках ниже показан процесс выполнения этой жадной стратегии.
|
||||
|
||||
1. В начальном состоянии указатели $i$ и $j$ стоят на двух концах массива.
|
||||
2. Вычислить вместимость текущего состояния $cap[i, j]$ и обновить максимальную вместимость.
|
||||
3. Сравнить высоты перегородок $i$ и $j$, после чего сдвинуть короткую перегородку на одну позицию внутрь.
|
||||
4. Повторять шаги `2.` и `3.` до тех пор, пока $i$ и $j$ не встретятся.
|
||||
|
||||
=== "<1>"
|
||||
{ class="animation-figure" }
|
||||
|
||||
=== "<2>"
|
||||
{ class="animation-figure" }
|
||||
|
||||
=== "<3>"
|
||||
{ class="animation-figure" }
|
||||
|
||||
=== "<4>"
|
||||
{ class="animation-figure" }
|
||||
|
||||
=== "<5>"
|
||||
{ class="animation-figure" }
|
||||
|
||||
=== "<6>"
|
||||
{ class="animation-figure" }
|
||||
|
||||
=== "<7>"
|
||||
{ class="animation-figure" }
|
||||
|
||||
=== "<8>"
|
||||
{ class="animation-figure" }
|
||||
|
||||
=== "<9>"
|
||||
{ class="animation-figure" }
|
||||
|
||||
<p align="center"> Рисунок 15-11 Жадный процесс решения задачи о максимальной вместимости </p>
|
||||
|
||||
### 2. Код реализации
|
||||
|
||||
Цикл в коде выполняется не более $n$ раз, **поэтому временная сложность равна $O(n)$**.
|
||||
|
||||
Переменные $i$, $j$, $res$ используют дополнительную память постоянного размера, **поэтому пространственная сложность равна $O(1)$**.
|
||||
|
||||
=== "Python"
|
||||
|
||||
```python title="max_capacity.py"
|
||||
def max_capacity(ht: list[int]) -> int:
|
||||
"""Максимальная вместимость: жадный алгоритм"""
|
||||
# Инициализировать i и j так, чтобы они располагались по двум концам массива
|
||||
i, j = 0, len(ht) - 1
|
||||
# Начальная максимальная вместимость равна 0
|
||||
res = 0
|
||||
# Выполнять жадный выбор в цикле, пока две доски не встретятся
|
||||
while i < j:
|
||||
# Обновить максимальную вместимость
|
||||
cap = min(ht[i], ht[j]) * (j - i)
|
||||
res = max(res, cap)
|
||||
# Сдвигать внутрь более короткую сторону
|
||||
if ht[i] < ht[j]:
|
||||
i += 1
|
||||
else:
|
||||
j -= 1
|
||||
return res
|
||||
```
|
||||
|
||||
=== "C++"
|
||||
|
||||
```cpp title="max_capacity.cpp"
|
||||
/* Максимальная вместимость: жадный алгоритм */
|
||||
int maxCapacity(vector<int> &ht) {
|
||||
// Инициализировать i и j так, чтобы они располагались по двум концам массива
|
||||
int i = 0, j = ht.size() - 1;
|
||||
// Начальная максимальная вместимость равна 0
|
||||
int res = 0;
|
||||
// Выполнять жадный выбор в цикле, пока две доски не встретятся
|
||||
while (i < j) {
|
||||
// Обновить максимальную вместимость
|
||||
int cap = min(ht[i], ht[j]) * (j - i);
|
||||
res = max(res, cap);
|
||||
// Сдвигать внутрь более короткую сторону
|
||||
if (ht[i] < ht[j]) {
|
||||
i++;
|
||||
} else {
|
||||
j--;
|
||||
}
|
||||
}
|
||||
return res;
|
||||
}
|
||||
```
|
||||
|
||||
=== "Java"
|
||||
|
||||
```java title="max_capacity.java"
|
||||
/* Максимальная вместимость: жадный алгоритм */
|
||||
int maxCapacity(int[] ht) {
|
||||
// Инициализировать i и j так, чтобы они располагались по двум концам массива
|
||||
int i = 0, j = ht.length - 1;
|
||||
// Начальная максимальная вместимость равна 0
|
||||
int res = 0;
|
||||
// Выполнять жадный выбор в цикле, пока две доски не встретятся
|
||||
while (i < j) {
|
||||
// Обновить максимальную вместимость
|
||||
int cap = Math.min(ht[i], ht[j]) * (j - i);
|
||||
res = Math.max(res, cap);
|
||||
// Сдвигать внутрь более короткую сторону
|
||||
if (ht[i] < ht[j]) {
|
||||
i++;
|
||||
} else {
|
||||
j--;
|
||||
}
|
||||
}
|
||||
return res;
|
||||
}
|
||||
```
|
||||
|
||||
=== "C#"
|
||||
|
||||
```csharp title="max_capacity.cs"
|
||||
/* Максимальная вместимость: жадный алгоритм */
|
||||
int MaxCapacity(int[] ht) {
|
||||
// Инициализировать i и j так, чтобы они располагались по двум концам массива
|
||||
int i = 0, j = ht.Length - 1;
|
||||
// Начальная максимальная вместимость равна 0
|
||||
int res = 0;
|
||||
// Выполнять жадный выбор в цикле, пока две доски не встретятся
|
||||
while (i < j) {
|
||||
// Обновить максимальную вместимость
|
||||
int cap = Math.Min(ht[i], ht[j]) * (j - i);
|
||||
res = Math.Max(res, cap);
|
||||
// Сдвигать внутрь более короткую сторону
|
||||
if (ht[i] < ht[j]) {
|
||||
i++;
|
||||
} else {
|
||||
j--;
|
||||
}
|
||||
}
|
||||
return res;
|
||||
}
|
||||
```
|
||||
|
||||
=== "Go"
|
||||
|
||||
```go title="max_capacity.go"
|
||||
/* Максимальная вместимость: жадный алгоритм */
|
||||
func maxCapacity(ht []int) int {
|
||||
// Инициализировать i и j так, чтобы они располагались по двум концам массива
|
||||
i, j := 0, len(ht)-1
|
||||
// Начальная максимальная вместимость равна 0
|
||||
res := 0
|
||||
// Выполнять жадный выбор в цикле, пока две доски не встретятся
|
||||
for i < j {
|
||||
// Обновить максимальную вместимость
|
||||
capacity := int(math.Min(float64(ht[i]), float64(ht[j]))) * (j - i)
|
||||
res = int(math.Max(float64(res), float64(capacity)))
|
||||
// Сдвигать внутрь более короткую сторону
|
||||
if ht[i] < ht[j] {
|
||||
i++
|
||||
} else {
|
||||
j--
|
||||
}
|
||||
}
|
||||
return res
|
||||
}
|
||||
```
|
||||
|
||||
=== "Swift"
|
||||
|
||||
```swift title="max_capacity.swift"
|
||||
/* Максимальная вместимость: жадный алгоритм */
|
||||
func maxCapacity(ht: [Int]) -> Int {
|
||||
// Инициализировать i и j так, чтобы они располагались по двум концам массива
|
||||
var i = ht.startIndex, j = ht.endIndex - 1
|
||||
// Начальная максимальная вместимость равна 0
|
||||
var res = 0
|
||||
// Выполнять жадный выбор в цикле, пока две доски не встретятся
|
||||
while i < j {
|
||||
// Обновить максимальную вместимость
|
||||
let cap = min(ht[i], ht[j]) * (j - i)
|
||||
res = max(res, cap)
|
||||
// Сдвигать внутрь более короткую сторону
|
||||
if ht[i] < ht[j] {
|
||||
i += 1
|
||||
} else {
|
||||
j -= 1
|
||||
}
|
||||
}
|
||||
return res
|
||||
}
|
||||
```
|
||||
|
||||
=== "JS"
|
||||
|
||||
```javascript title="max_capacity.js"
|
||||
/* Максимальная вместимость: жадный алгоритм */
|
||||
function maxCapacity(ht) {
|
||||
// Инициализировать i и j так, чтобы они располагались по двум концам массива
|
||||
let i = 0,
|
||||
j = ht.length - 1;
|
||||
// Начальная максимальная вместимость равна 0
|
||||
let res = 0;
|
||||
// Выполнять жадный выбор в цикле, пока две доски не встретятся
|
||||
while (i < j) {
|
||||
// Обновить максимальную вместимость
|
||||
const cap = Math.min(ht[i], ht[j]) * (j - i);
|
||||
res = Math.max(res, cap);
|
||||
// Сдвигать внутрь более короткую сторону
|
||||
if (ht[i] < ht[j]) {
|
||||
i += 1;
|
||||
} else {
|
||||
j -= 1;
|
||||
}
|
||||
}
|
||||
return res;
|
||||
}
|
||||
```
|
||||
|
||||
=== "TS"
|
||||
|
||||
```typescript title="max_capacity.ts"
|
||||
/* Максимальная вместимость: жадный алгоритм */
|
||||
function maxCapacity(ht: number[]): number {
|
||||
// Инициализировать i и j так, чтобы они располагались по двум концам массива
|
||||
let i = 0,
|
||||
j = ht.length - 1;
|
||||
// Начальная максимальная вместимость равна 0
|
||||
let res = 0;
|
||||
// Выполнять жадный выбор в цикле, пока две доски не встретятся
|
||||
while (i < j) {
|
||||
// Обновить максимальную вместимость
|
||||
const cap: number = Math.min(ht[i], ht[j]) * (j - i);
|
||||
res = Math.max(res, cap);
|
||||
// Сдвигать внутрь более короткую сторону
|
||||
if (ht[i] < ht[j]) {
|
||||
i += 1;
|
||||
} else {
|
||||
j -= 1;
|
||||
}
|
||||
}
|
||||
return res;
|
||||
}
|
||||
```
|
||||
|
||||
=== "Dart"
|
||||
|
||||
```dart title="max_capacity.dart"
|
||||
/* Максимальная вместимость: жадный алгоритм */
|
||||
int maxCapacity(List<int> ht) {
|
||||
// Инициализировать i и j так, чтобы они располагались по двум концам массива
|
||||
int i = 0, j = ht.length - 1;
|
||||
// Начальная максимальная вместимость равна 0
|
||||
int res = 0;
|
||||
// Выполнять жадный выбор в цикле, пока две доски не встретятся
|
||||
while (i < j) {
|
||||
// Обновить максимальную вместимость
|
||||
int cap = min(ht[i], ht[j]) * (j - i);
|
||||
res = max(res, cap);
|
||||
// Сдвигать внутрь более короткую сторону
|
||||
if (ht[i] < ht[j]) {
|
||||
i++;
|
||||
} else {
|
||||
j--;
|
||||
}
|
||||
}
|
||||
return res;
|
||||
}
|
||||
```
|
||||
|
||||
=== "Rust"
|
||||
|
||||
```rust title="max_capacity.rs"
|
||||
/* Максимальная вместимость: жадный алгоритм */
|
||||
fn max_capacity(ht: &[i32]) -> i32 {
|
||||
// Инициализировать i и j так, чтобы они располагались по двум концам массива
|
||||
let mut i = 0;
|
||||
let mut j = ht.len() - 1;
|
||||
// Начальная максимальная вместимость равна 0
|
||||
let mut res = 0;
|
||||
// Выполнять жадный выбор в цикле, пока две доски не встретятся
|
||||
while i < j {
|
||||
// Обновить максимальную вместимость
|
||||
let cap = std::cmp::min(ht[i], ht[j]) * (j - i) as i32;
|
||||
res = std::cmp::max(res, cap);
|
||||
// Сдвигать внутрь более короткую сторону
|
||||
if ht[i] < ht[j] {
|
||||
i += 1;
|
||||
} else {
|
||||
j -= 1;
|
||||
}
|
||||
}
|
||||
res
|
||||
}
|
||||
```
|
||||
|
||||
=== "C"
|
||||
|
||||
```c title="max_capacity.c"
|
||||
/* Максимальная вместимость: жадный алгоритм */
|
||||
int maxCapacity(int ht[], int htLength) {
|
||||
// Инициализировать i и j так, чтобы они располагались по двум концам массива
|
||||
int i = 0;
|
||||
int j = htLength - 1;
|
||||
// Начальная максимальная вместимость равна 0
|
||||
int res = 0;
|
||||
// Выполнять жадный выбор в цикле, пока две доски не встретятся
|
||||
while (i < j) {
|
||||
// Обновить максимальную вместимость
|
||||
int capacity = myMin(ht[i], ht[j]) * (j - i);
|
||||
res = myMax(res, capacity);
|
||||
// Сдвигать внутрь более короткую сторону
|
||||
if (ht[i] < ht[j]) {
|
||||
i++;
|
||||
} else {
|
||||
j--;
|
||||
}
|
||||
}
|
||||
return res;
|
||||
}
|
||||
```
|
||||
|
||||
=== "Kotlin"
|
||||
|
||||
```kotlin title="max_capacity.kt"
|
||||
/* Максимальная вместимость: жадный алгоритм */
|
||||
fun maxCapacity(ht: IntArray): Int {
|
||||
// Инициализировать i и j так, чтобы они располагались по двум концам массива
|
||||
var i = 0
|
||||
var j = ht.size - 1
|
||||
// Начальная максимальная вместимость равна 0
|
||||
var res = 0
|
||||
// Выполнять жадный выбор в цикле, пока две доски не встретятся
|
||||
while (i < j) {
|
||||
// Обновить максимальную вместимость
|
||||
val cap = min(ht[i], ht[j]) * (j - i)
|
||||
res = max(res, cap)
|
||||
// Сдвигать внутрь более короткую сторону
|
||||
if (ht[i] < ht[j]) {
|
||||
i++
|
||||
} else {
|
||||
j--
|
||||
}
|
||||
}
|
||||
return res
|
||||
}
|
||||
```
|
||||
|
||||
=== "Ruby"
|
||||
|
||||
```ruby title="max_capacity.rb"
|
||||
=begin
|
||||
File: max_capacity.rb
|
||||
Created Time: 2024-05-07
|
||||
Author: Xuan Khoa Tu Nguyen (ngxktuzkai2000@gmail.com)
|
||||
=end
|
||||
|
||||
# ## Максимальная вместимость: жадный алгоритм ###
|
||||
def max_capacity(ht)
|
||||
# Инициализировать i и j так, чтобы они располагались по двум концам массива
|
||||
i, j = 0, ht.length - 1
|
||||
# Начальная максимальная вместимость равна 0
|
||||
res = 0
|
||||
|
||||
# Выполнять жадный выбор в цикле, пока две доски не встретятся
|
||||
while i < j
|
||||
# Обновить максимальную вместимость
|
||||
cap = [ht[i], ht[j]].min * (j - i)
|
||||
res = [res, cap].max
|
||||
# Сдвигать внутрь более короткую сторону
|
||||
if ht[i] < ht[j]
|
||||
i += 1
|
||||
else
|
||||
j -= 1
|
||||
end
|
||||
end
|
||||
|
||||
res
|
||||
end
|
||||
```
|
||||
|
||||
??? pythontutor "Визуализация кода"
|
||||
|
||||
<div style="height: 549px; width: 100%;"><iframe class="pythontutor-iframe" src="https://pythontutor.com/iframe-embed.html#code=def%20max_capacity%28ht%3A%20list%5Bint%5D%29%20-%3E%20int%3A%0A%20%20%20%20%22%22%22%D0%9C%D0%B0%D0%BA%D1%81%D0%B8%D0%BC%D0%B0%D0%BB%D1%8C%D0%BD%D0%B0%D1%8F%20%D0%B2%D0%BC%D0%B5%D1%81%D1%82%D0%B8%D0%BC%D0%BE%D1%81%D1%82%D1%8C%3A%20%D0%B6%D0%B0%D0%B4%D0%BD%D1%8B%D0%B9%20%D0%B0%D0%BB%D0%B3%D0%BE%D1%80%D0%B8%D1%82%D0%BC%22%22%22%0A%20%20%20%20%23%20%D0%98%D0%BD%D0%B8%D1%86%D0%B8%D0%B0%D0%BB%D0%B8%D0%B7%D0%B8%D1%80%D0%BE%D0%B2%D0%B0%D1%82%D1%8C%20i%20%D0%B8%20j%20%D1%82%D0%B0%D0%BA%2C%20%D1%87%D1%82%D0%BE%D0%B1%D1%8B%20%D0%BE%D0%BD%D0%B8%20%D1%80%D0%B0%D1%81%D0%BF%D0%BE%D0%BB%D0%B0%D0%B3%D0%B0%D0%BB%D0%B8%D1%81%D1%8C%20%D0%BF%D0%BE%20%D0%B4%D0%B2%D1%83%D0%BC%20%D0%BA%D0%BE%D0%BD%D1%86%D0%B0%D0%BC%20%D0%BC%D0%B0%D1%81%D1%81%D0%B8%D0%B2%D0%B0%0A%20%20%20%20i%2C%20j%20%3D%200%2C%20len%28ht%29%20-%201%0A%20%20%20%20%23%20%D0%9D%D0%B0%D1%87%D0%B0%D0%BB%D1%8C%D0%BD%D0%B0%D1%8F%20%D0%BC%D0%B0%D0%BA%D1%81%D0%B8%D0%BC%D0%B0%D0%BB%D1%8C%D0%BD%D0%B0%D1%8F%20%D0%B2%D0%BC%D0%B5%D1%81%D1%82%D0%B8%D0%BC%D0%BE%D1%81%D1%82%D1%8C%20%D1%80%D0%B0%D0%B2%D0%BD%D0%B0%200%0A%20%20%20%20res%20%3D%200%0A%20%20%20%20%23%20%D0%92%D1%8B%D0%BF%D0%BE%D0%BB%D0%BD%D1%8F%D1%82%D1%8C%20%D0%B6%D0%B0%D0%B4%D0%BD%D1%8B%D0%B9%20%D0%B2%D1%8B%D0%B1%D0%BE%D1%80%20%D0%B2%20%D1%86%D0%B8%D0%BA%D0%BB%D0%B5%2C%20%D0%BF%D0%BE%D0%BA%D0%B0%20%D0%B4%D0%B2%D0%B5%20%D0%B4%D0%BE%D1%81%D0%BA%D0%B8%20%D0%BD%D0%B5%20%D0%B2%D1%81%D1%82%D1%80%D0%B5%D1%82%D1%8F%D1%82%D1%81%D1%8F%0A%20%20%20%20while%20i%20%3C%20j%3A%0A%20%20%20%20%20%20%20%20%23%20%D0%9E%D0%B1%D0%BD%D0%BE%D0%B2%D0%B8%D1%82%D1%8C%20%D0%BC%D0%B0%D0%BA%D1%81%D0%B8%D0%BC%D0%B0%D0%BB%D1%8C%D0%BD%D1%83%D1%8E%20%D0%B2%D0%BC%D0%B5%D1%81%D1%82%D0%B8%D0%BC%D0%BE%D1%81%D1%82%D1%8C%0A%20%20%20%20%20%20%20%20cap%20%3D%20min%28ht%5Bi%5D%2C%20ht%5Bj%5D%29%20%2A%20%28j%20-%20i%29%0A%20%20%20%20%20%20%20%20res%20%3D%20max%28res%2C%20cap%29%0A%20%20%20%20%20%20%20%20%23%20%D0%A1%D0%B4%D0%B2%D0%B8%D0%B3%D0%B0%D1%82%D1%8C%20%D0%B2%D0%BD%D1%83%D1%82%D1%80%D1%8C%20%D0%B1%D0%BE%D0%BB%D0%B5%D0%B5%20%D0%BA%D0%BE%D1%80%D0%BE%D1%82%D0%BA%D1%83%D1%8E%20%D1%81%D1%82%D0%BE%D1%80%D0%BE%D0%BD%D1%83%0A%20%20%20%20%20%20%20%20if%20ht%5Bi%5D%20%3C%20ht%5Bj%5D%3A%0A%20%20%20%20%20%20%20%20%20%20%20%20i%20%2B%3D%201%0A%20%20%20%20%20%20%20%20else%3A%0A%20%20%20%20%20%20%20%20%20%20%20%20j%20-%3D%201%0A%20%20%20%20return%20res%0A%0A%0A%22%22%22Driver%20Code%22%22%22%0Aif%20__name__%20%3D%3D%20%22__main__%22%3A%0A%20%20%20%20ht%20%3D%20%5B3%2C%208%2C%205%2C%202%2C%207%2C%207%2C%203%2C%204%5D%0A%0A%20%20%20%20%23%20%D0%96%D0%B0%D0%B4%D0%BD%D1%8B%D0%B9%20%D0%B0%D0%BB%D0%B3%D0%BE%D1%80%D0%B8%D1%82%D0%BC%0A%20%20%20%20res%20%3D%20max_capacity%28ht%29%0A%20%20%20%20print%28f%22%D0%9C%D0%B0%D0%BA%D1%81%D0%B8%D0%BC%D0%B0%D0%BB%D1%8C%D0%BD%D0%B0%D1%8F%20%D0%B2%D0%BC%D0%B5%D1%81%D1%82%D0%B8%D0%BC%D0%BE%D1%81%D1%82%D1%8C%20%3D%20%7Bres%7D%22%29&codeDivHeight=472&codeDivWidth=350&cumulative=false&curInstr=4&heapPrimitives=nevernest&origin=opt-frontend.js&py=311&rawInputLstJSON=%5B%5D&textReferences=false"> </iframe></div>
|
||||
<div style="margin-top: 5px;"><a href="https://pythontutor.com/iframe-embed.html#code=def%20max_capacity%28ht%3A%20list%5Bint%5D%29%20-%3E%20int%3A%0A%20%20%20%20%22%22%22%D0%9C%D0%B0%D0%BA%D1%81%D0%B8%D0%BC%D0%B0%D0%BB%D1%8C%D0%BD%D0%B0%D1%8F%20%D0%B2%D0%BC%D0%B5%D1%81%D1%82%D0%B8%D0%BC%D0%BE%D1%81%D1%82%D1%8C%3A%20%D0%B6%D0%B0%D0%B4%D0%BD%D1%8B%D0%B9%20%D0%B0%D0%BB%D0%B3%D0%BE%D1%80%D0%B8%D1%82%D0%BC%22%22%22%0A%20%20%20%20%23%20%D0%98%D0%BD%D0%B8%D1%86%D0%B8%D0%B0%D0%BB%D0%B8%D0%B7%D0%B8%D1%80%D0%BE%D0%B2%D0%B0%D1%82%D1%8C%20i%20%D0%B8%20j%20%D1%82%D0%B0%D0%BA%2C%20%D1%87%D1%82%D0%BE%D0%B1%D1%8B%20%D0%BE%D0%BD%D0%B8%20%D1%80%D0%B0%D1%81%D0%BF%D0%BE%D0%BB%D0%B0%D0%B3%D0%B0%D0%BB%D0%B8%D1%81%D1%8C%20%D0%BF%D0%BE%20%D0%B4%D0%B2%D1%83%D0%BC%20%D0%BA%D0%BE%D0%BD%D1%86%D0%B0%D0%BC%20%D0%BC%D0%B0%D1%81%D1%81%D0%B8%D0%B2%D0%B0%0A%20%20%20%20i%2C%20j%20%3D%200%2C%20len%28ht%29%20-%201%0A%20%20%20%20%23%20%D0%9D%D0%B0%D1%87%D0%B0%D0%BB%D1%8C%D0%BD%D0%B0%D1%8F%20%D0%BC%D0%B0%D0%BA%D1%81%D0%B8%D0%BC%D0%B0%D0%BB%D1%8C%D0%BD%D0%B0%D1%8F%20%D0%B2%D0%BC%D0%B5%D1%81%D1%82%D0%B8%D0%BC%D0%BE%D1%81%D1%82%D1%8C%20%D1%80%D0%B0%D0%B2%D0%BD%D0%B0%200%0A%20%20%20%20res%20%3D%200%0A%20%20%20%20%23%20%D0%92%D1%8B%D0%BF%D0%BE%D0%BB%D0%BD%D1%8F%D1%82%D1%8C%20%D0%B6%D0%B0%D0%B4%D0%BD%D1%8B%D0%B9%20%D0%B2%D1%8B%D0%B1%D0%BE%D1%80%20%D0%B2%20%D1%86%D0%B8%D0%BA%D0%BB%D0%B5%2C%20%D0%BF%D0%BE%D0%BA%D0%B0%20%D0%B4%D0%B2%D0%B5%20%D0%B4%D0%BE%D1%81%D0%BA%D0%B8%20%D0%BD%D0%B5%20%D0%B2%D1%81%D1%82%D1%80%D0%B5%D1%82%D1%8F%D1%82%D1%81%D1%8F%0A%20%20%20%20while%20i%20%3C%20j%3A%0A%20%20%20%20%20%20%20%20%23%20%D0%9E%D0%B1%D0%BD%D0%BE%D0%B2%D0%B8%D1%82%D1%8C%20%D0%BC%D0%B0%D0%BA%D1%81%D0%B8%D0%BC%D0%B0%D0%BB%D1%8C%D0%BD%D1%83%D1%8E%20%D0%B2%D0%BC%D0%B5%D1%81%D1%82%D0%B8%D0%BC%D0%BE%D1%81%D1%82%D1%8C%0A%20%20%20%20%20%20%20%20cap%20%3D%20min%28ht%5Bi%5D%2C%20ht%5Bj%5D%29%20%2A%20%28j%20-%20i%29%0A%20%20%20%20%20%20%20%20res%20%3D%20max%28res%2C%20cap%29%0A%20%20%20%20%20%20%20%20%23%20%D0%A1%D0%B4%D0%B2%D0%B8%D0%B3%D0%B0%D1%82%D1%8C%20%D0%B2%D0%BD%D1%83%D1%82%D1%80%D1%8C%20%D0%B1%D0%BE%D0%BB%D0%B5%D0%B5%20%D0%BA%D0%BE%D1%80%D0%BE%D1%82%D0%BA%D1%83%D1%8E%20%D1%81%D1%82%D0%BE%D1%80%D0%BE%D0%BD%D1%83%0A%20%20%20%20%20%20%20%20if%20ht%5Bi%5D%20%3C%20ht%5Bj%5D%3A%0A%20%20%20%20%20%20%20%20%20%20%20%20i%20%2B%3D%201%0A%20%20%20%20%20%20%20%20else%3A%0A%20%20%20%20%20%20%20%20%20%20%20%20j%20-%3D%201%0A%20%20%20%20return%20res%0A%0A%0A%22%22%22Driver%20Code%22%22%22%0Aif%20__name__%20%3D%3D%20%22__main__%22%3A%0A%20%20%20%20ht%20%3D%20%5B3%2C%208%2C%205%2C%202%2C%207%2C%207%2C%203%2C%204%5D%0A%0A%20%20%20%20%23%20%D0%96%D0%B0%D0%B4%D0%BD%D1%8B%D0%B9%20%D0%B0%D0%BB%D0%B3%D0%BE%D1%80%D0%B8%D1%82%D0%BC%0A%20%20%20%20res%20%3D%20max_capacity%28ht%29%0A%20%20%20%20print%28f%22%D0%9C%D0%B0%D0%BA%D1%81%D0%B8%D0%BC%D0%B0%D0%BB%D1%8C%D0%BD%D0%B0%D1%8F%20%D0%B2%D0%BC%D0%B5%D1%81%D1%82%D0%B8%D0%BC%D0%BE%D1%81%D1%82%D1%8C%20%3D%20%7Bres%7D%22%29&codeDivHeight=800&codeDivWidth=600&cumulative=false&curInstr=4&heapPrimitives=nevernest&origin=opt-frontend.js&py=311&rawInputLstJSON=%5B%5D&textReferences=false" target="_blank" rel="noopener noreferrer">Во весь экран ></a></div>
|
||||
|
||||
### 3. Доказательство корректности
|
||||
|
||||
Жадный алгоритм быстрее полного перебора именно потому, что каждый жадный шаг «пропускает» часть состояний.
|
||||
|
||||
Например, в состоянии $cap[i, j]$ перегородка $i$ является короткой, а $j$ - длинной. Если жадно сдвинуть короткую перегородку $i$ на одну позицию внутрь, то состояния, показанные на рисунке 15-12, будут «пропущены». **Это означает, что позже мы уже не сможем проверить вместимость этих состояний**.
|
||||
|
||||
$$
|
||||
cap[i, i+1], cap[i, i+2], \dots, cap[i, j-2], cap[i, j-1]
|
||||
$$
|
||||
|
||||
{ class="animation-figure" }
|
||||
|
||||
<p align="center"> Рисунок 15-12 Состояния, пропущенные из-за смещения короткой перегородки </p>
|
||||
|
||||
Нетрудно заметить, что **эти пропущенные состояния на самом деле и есть все состояния, в которых длинная перегородка $j$ сдвигается внутрь**. Ранее мы уже доказали, что перемещение длинной перегородки внутрь обязательно уменьшает вместимость. Иными словами, пропущенные состояния не могут быть оптимальным решением, **поэтому их пропуск не приводит к потере оптимума**.
|
||||
|
||||
Приведенный анализ показывает, что операция перемещения короткой перегородки является «безопасной», а жадная стратегия действительно эффективна.
|
||||
@@ -0,0 +1,417 @@
|
||||
---
|
||||
comments: true
|
||||
---
|
||||
|
||||
# 15.4 Задача о максимальном произведении разбиения
|
||||
|
||||
!!! question
|
||||
|
||||
Дан положительный целый $n$. Требуется разложить его в сумму как минимум двух положительных целых чисел и найти максимально возможное произведение всех полученных чисел, как показано на рисунке 15-13.
|
||||
|
||||
{ class="animation-figure" }
|
||||
|
||||
<p align="center"> Рисунок 15-13 Определение задачи о максимальном произведении разбиения </p>
|
||||
|
||||
Предположим, что мы разбили $n$ на $m$ целочисленных множителей, где $i$-й множитель обозначим через $n_i$, то есть
|
||||
|
||||
$$
|
||||
n = \sum_{i=1}^{m}n_i
|
||||
$$
|
||||
|
||||
Цель задачи - найти максимальное произведение всех целочисленных множителей, то есть
|
||||
|
||||
$$
|
||||
\max(\prod_{i=1}^{m}n_i)
|
||||
$$
|
||||
|
||||
Нужно понять: каким должно быть число частей $m$ и какими должны быть значения каждого $n_i$?
|
||||
|
||||
### 1. Определение жадной стратегии
|
||||
|
||||
Из опыта известно, что произведение двух целых чисел часто больше их суммы. Предположим, что мы выделяем из $n$ множитель $2$, тогда произведение равно $2(n-2)$. Сравним это выражение с $n$:
|
||||
|
||||
$$
|
||||
\begin{aligned}
|
||||
2(n-2) & \geq n \newline
|
||||
2n - n - 4 & \geq 0 \newline
|
||||
n & \geq 4
|
||||
\end{aligned}
|
||||
$$
|
||||
|
||||
Как показано на рисунке 15-14, когда $n \geq 4$, выделение множителя $2$ увеличивает произведение. **Это означает, что все целые числа, большие либо равные $4$, следует продолжать разбивать**.
|
||||
|
||||
**Жадная стратегия 1**: если в схеме разбиения присутствует множитель $\geq 4$, то его нужно дальше разбивать. В конечной схеме разбиения должны остаться только множители $1$, $2$, $3$.
|
||||
|
||||
{ class="animation-figure" }
|
||||
|
||||
<p align="center"> Рисунок 15-14 Разбиение увеличивает произведение </p>
|
||||
|
||||
Теперь подумаем, какой множитель является наилучшим. Среди $1$, $2$, $3$ очевидно худшим является $1$, потому что всегда выполняется $1 \times (n-1) < n$, то есть выделение $1$ уменьшает произведение.
|
||||
|
||||
Как показано на рисунке 15-15, при $n = 6$ имеем $3 \times 3 > 2 \times 2 \times 2$. **Это означает, что выделять $3$ выгоднее, чем выделять $2$**.
|
||||
|
||||
**Жадная стратегия 2**: в схеме разбиения должно быть не более двух множителей $2$. Потому что три двойки всегда можно заменить двумя тройками и получить большее произведение.
|
||||
|
||||
{ class="animation-figure" }
|
||||
|
||||
<p align="center"> Рисунок 15-15 Оптимальные множители разбиения </p>
|
||||
|
||||
Итак, получаем следующую жадную стратегию.
|
||||
|
||||
1. Для заданного целого $n$ непрерывно выделять из него множитель $3$, пока остаток не станет равным $0$, $1$ или $2$.
|
||||
2. Если остаток равен $0$, это означает, что $n$ кратно $3$, и больше ничего делать не нужно.
|
||||
3. Если остаток равен $2$, дальнейшее разбиение не требуется, его нужно сохранить.
|
||||
4. Если остаток равен $1$, то поскольку $2 \times 2 > 1 \times 3$, последний множитель $3$ следует заменить на $2$.
|
||||
|
||||
### 2. Код реализации
|
||||
|
||||
Как показано на рисунке 15-16, нам не нужен цикл, чтобы выполнять разбиение числа. Можно использовать целочисленное деление вниз, чтобы получить число троек $a$, и операцию взятия остатка, чтобы получить остаток $b$. Тогда имеем:
|
||||
|
||||
$$
|
||||
n = 3 a + b
|
||||
$$
|
||||
|
||||
Обратите внимание, что для граничного случая $n \leq 3$ необходимо выделить множитель $1$, и тогда произведение равно $1 \times (n - 1)$.
|
||||
|
||||
=== "Python"
|
||||
|
||||
```python title="max_product_cutting.py"
|
||||
def max_product_cutting(n: int) -> int:
|
||||
"""Максимальное произведение разрезания: жадный алгоритм"""
|
||||
# Когда n <= 3, обязательно нужно выделить одну 1
|
||||
if n <= 3:
|
||||
return 1 * (n - 1)
|
||||
# Жадно выделить множители 3, где a — число троек, а b — остаток
|
||||
a, b = n // 3, n % 3
|
||||
if b == 1:
|
||||
# Если остаток равен 1, преобразовать одну пару 1 * 3 в 2 * 2
|
||||
return int(math.pow(3, a - 1)) * 2 * 2
|
||||
if b == 2:
|
||||
# Если остаток равен 2, ничего не делать
|
||||
return int(math.pow(3, a)) * 2
|
||||
# Если остаток равен 0, ничего не делать
|
||||
return int(math.pow(3, a))
|
||||
```
|
||||
|
||||
=== "C++"
|
||||
|
||||
```cpp title="max_product_cutting.cpp"
|
||||
/* Максимальное произведение разрезания: жадный алгоритм */
|
||||
int maxProductCutting(int n) {
|
||||
// Когда n <= 3, обязательно нужно выделить одну 1
|
||||
if (n <= 3) {
|
||||
return 1 * (n - 1);
|
||||
}
|
||||
// Жадно выделить множители 3, где a — число троек, а b — остаток
|
||||
int a = n / 3;
|
||||
int b = n % 3;
|
||||
if (b == 1) {
|
||||
// Если остаток равен 1, преобразовать одну пару 1 * 3 в 2 * 2
|
||||
return (int)pow(3, a - 1) * 2 * 2;
|
||||
}
|
||||
if (b == 2) {
|
||||
// Если остаток равен 2, ничего не делать
|
||||
return (int)pow(3, a) * 2;
|
||||
}
|
||||
// Если остаток равен 0, ничего не делать
|
||||
return (int)pow(3, a);
|
||||
}
|
||||
```
|
||||
|
||||
=== "Java"
|
||||
|
||||
```java title="max_product_cutting.java"
|
||||
/* Максимальное произведение разрезания: жадный алгоритм */
|
||||
int maxProductCutting(int n) {
|
||||
// Когда n <= 3, обязательно нужно выделить одну 1
|
||||
if (n <= 3) {
|
||||
return 1 * (n - 1);
|
||||
}
|
||||
// Жадно выделить множители 3, где a — число троек, а b — остаток
|
||||
int a = n / 3;
|
||||
int b = n % 3;
|
||||
if (b == 1) {
|
||||
// Если остаток равен 1, преобразовать одну пару 1 * 3 в 2 * 2
|
||||
return (int) Math.pow(3, a - 1) * 2 * 2;
|
||||
}
|
||||
if (b == 2) {
|
||||
// Если остаток равен 2, ничего не делать
|
||||
return (int) Math.pow(3, a) * 2;
|
||||
}
|
||||
// Если остаток равен 0, ничего не делать
|
||||
return (int) Math.pow(3, a);
|
||||
}
|
||||
```
|
||||
|
||||
=== "C#"
|
||||
|
||||
```csharp title="max_product_cutting.cs"
|
||||
/* Максимальное произведение разрезания: жадный алгоритм */
|
||||
int MaxProductCutting(int n) {
|
||||
// Когда n <= 3, обязательно нужно выделить одну 1
|
||||
if (n <= 3) {
|
||||
return 1 * (n - 1);
|
||||
}
|
||||
// Жадно выделить множители 3, где a — число троек, а b — остаток
|
||||
int a = n / 3;
|
||||
int b = n % 3;
|
||||
if (b == 1) {
|
||||
// Если остаток равен 1, преобразовать одну пару 1 * 3 в 2 * 2
|
||||
return (int)Math.Pow(3, a - 1) * 2 * 2;
|
||||
}
|
||||
if (b == 2) {
|
||||
// Если остаток равен 2, ничего не делать
|
||||
return (int)Math.Pow(3, a) * 2;
|
||||
}
|
||||
// Если остаток равен 0, ничего не делать
|
||||
return (int)Math.Pow(3, a);
|
||||
}
|
||||
```
|
||||
|
||||
=== "Go"
|
||||
|
||||
```go title="max_product_cutting.go"
|
||||
/* Максимальное произведение разрезания: жадный алгоритм */
|
||||
func maxProductCutting(n int) int {
|
||||
// Когда n <= 3, обязательно нужно выделить одну 1
|
||||
if n <= 3 {
|
||||
return 1 * (n - 1)
|
||||
}
|
||||
// Жадно выделить множители 3, где a — число троек, а b — остаток
|
||||
a := n / 3
|
||||
b := n % 3
|
||||
if b == 1 {
|
||||
// Если остаток равен 1, преобразовать одну пару 1 * 3 в 2 * 2
|
||||
return int(math.Pow(3, float64(a-1))) * 2 * 2
|
||||
}
|
||||
if b == 2 {
|
||||
// Если остаток равен 2, ничего не делать
|
||||
return int(math.Pow(3, float64(a))) * 2
|
||||
}
|
||||
// Если остаток равен 0, ничего не делать
|
||||
return int(math.Pow(3, float64(a)))
|
||||
}
|
||||
```
|
||||
|
||||
=== "Swift"
|
||||
|
||||
```swift title="max_product_cutting.swift"
|
||||
/* Максимальное произведение разрезания: жадный алгоритм */
|
||||
func maxProductCutting(n: Int) -> Int {
|
||||
// Когда n <= 3, обязательно нужно выделить одну 1
|
||||
if n <= 3 {
|
||||
return 1 * (n - 1)
|
||||
}
|
||||
// Жадно выделить множители 3, где a — число троек, а b — остаток
|
||||
let a = n / 3
|
||||
let b = n % 3
|
||||
if b == 1 {
|
||||
// Если остаток равен 1, преобразовать одну пару 1 * 3 в 2 * 2
|
||||
return pow(3, a - 1) * 2 * 2
|
||||
}
|
||||
if b == 2 {
|
||||
// Если остаток равен 2, ничего не делать
|
||||
return pow(3, a) * 2
|
||||
}
|
||||
// Если остаток равен 0, ничего не делать
|
||||
return pow(3, a)
|
||||
}
|
||||
```
|
||||
|
||||
=== "JS"
|
||||
|
||||
```javascript title="max_product_cutting.js"
|
||||
/* Максимальное произведение разрезания: жадный алгоритм */
|
||||
function maxProductCutting(n) {
|
||||
// Когда n <= 3, обязательно нужно выделить одну 1
|
||||
if (n <= 3) {
|
||||
return 1 * (n - 1);
|
||||
}
|
||||
// Жадно выделить множители 3, где a — число троек, а b — остаток
|
||||
let a = Math.floor(n / 3);
|
||||
let b = n % 3;
|
||||
if (b === 1) {
|
||||
// Если остаток равен 1, преобразовать одну пару 1 * 3 в 2 * 2
|
||||
return Math.pow(3, a - 1) * 2 * 2;
|
||||
}
|
||||
if (b === 2) {
|
||||
// Если остаток равен 2, ничего не делать
|
||||
return Math.pow(3, a) * 2;
|
||||
}
|
||||
// Если остаток равен 0, ничего не делать
|
||||
return Math.pow(3, a);
|
||||
}
|
||||
```
|
||||
|
||||
=== "TS"
|
||||
|
||||
```typescript title="max_product_cutting.ts"
|
||||
/* Максимальное произведение разрезания: жадный алгоритм */
|
||||
function maxProductCutting(n: number): number {
|
||||
// Когда n <= 3, обязательно нужно выделить одну 1
|
||||
if (n <= 3) {
|
||||
return 1 * (n - 1);
|
||||
}
|
||||
// Жадно выделить множители 3, где a — число троек, а b — остаток
|
||||
let a: number = Math.floor(n / 3);
|
||||
let b: number = n % 3;
|
||||
if (b === 1) {
|
||||
// Если остаток равен 1, преобразовать одну пару 1 * 3 в 2 * 2
|
||||
return Math.pow(3, a - 1) * 2 * 2;
|
||||
}
|
||||
if (b === 2) {
|
||||
// Если остаток равен 2, ничего не делать
|
||||
return Math.pow(3, a) * 2;
|
||||
}
|
||||
// Если остаток равен 0, ничего не делать
|
||||
return Math.pow(3, a);
|
||||
}
|
||||
```
|
||||
|
||||
=== "Dart"
|
||||
|
||||
```dart title="max_product_cutting.dart"
|
||||
/* Максимальное произведение разрезания: жадный алгоритм */
|
||||
int maxProductCutting(int n) {
|
||||
// Когда n <= 3, обязательно нужно выделить одну 1
|
||||
if (n <= 3) {
|
||||
return 1 * (n - 1);
|
||||
}
|
||||
// Жадно выделить множители 3, где a — число троек, а b — остаток
|
||||
int a = n ~/ 3;
|
||||
int b = n % 3;
|
||||
if (b == 1) {
|
||||
// Если остаток равен 1, преобразовать одну пару 1 * 3 в 2 * 2
|
||||
return (pow(3, a - 1) * 2 * 2).toInt();
|
||||
}
|
||||
if (b == 2) {
|
||||
// Если остаток равен 2, ничего не делать
|
||||
return (pow(3, a) * 2).toInt();
|
||||
}
|
||||
// Если остаток равен 0, ничего не делать
|
||||
return pow(3, a).toInt();
|
||||
}
|
||||
```
|
||||
|
||||
=== "Rust"
|
||||
|
||||
```rust title="max_product_cutting.rs"
|
||||
/* Максимальное произведение разрезания: жадный алгоритм */
|
||||
fn max_product_cutting(n: i32) -> i32 {
|
||||
// Когда n <= 3, обязательно нужно выделить одну 1
|
||||
if n <= 3 {
|
||||
return 1 * (n - 1);
|
||||
}
|
||||
// Жадно выделить множители 3, где a — число троек, а b — остаток
|
||||
let a = n / 3;
|
||||
let b = n % 3;
|
||||
if b == 1 {
|
||||
// Если остаток равен 1, преобразовать одну пару 1 * 3 в 2 * 2
|
||||
3_i32.pow(a as u32 - 1) * 2 * 2
|
||||
} else if b == 2 {
|
||||
// Если остаток равен 2, ничего не делать
|
||||
3_i32.pow(a as u32) * 2
|
||||
} else {
|
||||
// Если остаток равен 0, ничего не делать
|
||||
3_i32.pow(a as u32)
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
=== "C"
|
||||
|
||||
```c title="max_product_cutting.c"
|
||||
/* Максимальное произведение разрезания: жадный алгоритм */
|
||||
int maxProductCutting(int n) {
|
||||
// Когда n <= 3, обязательно нужно выделить одну 1
|
||||
if (n <= 3) {
|
||||
return 1 * (n - 1);
|
||||
}
|
||||
// Жадно выделить множители 3, где a — число троек, а b — остаток
|
||||
int a = n / 3;
|
||||
int b = n % 3;
|
||||
if (b == 1) {
|
||||
// Если остаток равен 1, преобразовать одну пару 1 * 3 в 2 * 2
|
||||
return pow(3, a - 1) * 2 * 2;
|
||||
}
|
||||
if (b == 2) {
|
||||
// Если остаток равен 2, ничего не делать
|
||||
return pow(3, a) * 2;
|
||||
}
|
||||
// Если остаток равен 0, ничего не делать
|
||||
return pow(3, a);
|
||||
}
|
||||
```
|
||||
|
||||
=== "Kotlin"
|
||||
|
||||
```kotlin title="max_product_cutting.kt"
|
||||
/* Максимальное произведение разрезания: жадный алгоритм */
|
||||
fun maxProductCutting(n: Int): Int {
|
||||
// Когда n <= 3, обязательно нужно выделить одну 1
|
||||
if (n <= 3) {
|
||||
return 1 * (n - 1)
|
||||
}
|
||||
// Жадно выделить множители 3, где a — число троек, а b — остаток
|
||||
val a = n / 3
|
||||
val b = n % 3
|
||||
if (b == 1) {
|
||||
// Если остаток равен 1, преобразовать одну пару 1 * 3 в 2 * 2
|
||||
return 3.0.pow((a - 1)).toInt() * 2 * 2
|
||||
}
|
||||
if (b == 2) {
|
||||
// Если остаток равен 2, ничего не делать
|
||||
return 3.0.pow(a).toInt() * 2 * 2
|
||||
}
|
||||
// Если остаток равен 0, ничего не делать
|
||||
return 3.0.pow(a).toInt()
|
||||
}
|
||||
```
|
||||
|
||||
=== "Ruby"
|
||||
|
||||
```ruby title="max_product_cutting.rb"
|
||||
=begin
|
||||
File: max_product_cutting.rb
|
||||
Created Time: 2024-05-07
|
||||
Author: Xuan Khoa Tu Nguyen (ngxktuzkai2000@gmail.com)
|
||||
=end
|
||||
|
||||
# ## Максимальное произведение разрезания: жадный алгоритм ###
|
||||
def max_product_cutting(n)
|
||||
# Когда n <= 3, обязательно нужно выделить одну 1
|
||||
return 1 * (n - 1) if n <= 3
|
||||
# Жадно выделить множители 3, где a — число троек, а b — остаток
|
||||
a, b = n / 3, n % 3
|
||||
# Если остаток равен 1, преобразовать одну пару 1 * 3 в 2 * 2
|
||||
return (3.pow(a - 1) * 2 * 2).to_i if b == 1
|
||||
# Если остаток равен 2, ничего не делать
|
||||
return (3.pow(a) * 2).to_i if b == 2
|
||||
# Если остаток равен 0, ничего не делать
|
||||
3.pow(a).to_i
|
||||
end
|
||||
```
|
||||
|
||||
??? pythontutor "Визуализация кода"
|
||||
|
||||
<div style="height: 549px; width: 100%;"><iframe class="pythontutor-iframe" src="https://pythontutor.com/iframe-embed.html#code=import%20math%0A%0Adef%20max_product_cutting%28n%3A%20int%29%20-%3E%20int%3A%0A%20%20%20%20%22%22%22%D0%9C%D0%B0%D0%BA%D1%81%D0%B8%D0%BC%D0%B0%D0%BB%D1%8C%D0%BD%D0%BE%D0%B5%20%D0%BF%D1%80%D0%BE%D0%B8%D0%B7%D0%B2%D0%B5%D0%B4%D0%B5%D0%BD%D0%B8%D0%B5%20%D1%80%D0%B0%D0%B7%D1%80%D0%B5%D0%B7%D0%B0%D0%BD%D0%B8%D1%8F%3A%20%D0%B6%D0%B0%D0%B4%D0%BD%D1%8B%D0%B9%20%D0%B0%D0%BB%D0%B3%D0%BE%D1%80%D0%B8%D1%82%D0%BC%22%22%22%0A%20%20%20%20%23%20%D0%9A%D0%BE%D0%B3%D0%B4%D0%B0%20n%20%3C%3D%203%2C%20%D0%BE%D0%B1%D1%8F%D0%B7%D0%B0%D1%82%D0%B5%D0%BB%D1%8C%D0%BD%D0%BE%20%D0%BD%D1%83%D0%B6%D0%BD%D0%BE%20%D0%B2%D1%8B%D0%B4%D0%B5%D0%BB%D0%B8%D1%82%D1%8C%20%D0%BE%D0%B4%D0%BD%D1%83%201%0A%20%20%20%20if%20n%20%3C%3D%203%3A%0A%20%20%20%20%20%20%20%20return%201%20%2A%20%28n%20-%201%29%0A%20%20%20%20%23%20%D0%96%D0%B0%D0%B4%D0%BD%D0%BE%20%D0%B2%D1%8B%D0%B4%D0%B5%D0%BB%D0%B8%D1%82%D1%8C%20%D0%BC%D0%BD%D0%BE%D0%B6%D0%B8%D1%82%D0%B5%D0%BB%D0%B8%203%2C%20%D0%B3%D0%B4%D0%B5%20a%20%E2%80%94%20%D1%87%D0%B8%D1%81%D0%BB%D0%BE%20%D1%82%D1%80%D0%BE%D0%B5%D0%BA%2C%20%D0%B0%20b%20%E2%80%94%20%D0%BE%D1%81%D1%82%D0%B0%D1%82%D0%BE%D0%BA%0A%20%20%20%20a%2C%20b%20%3D%20n%20%2F%2F%203%2C%20n%20%25%203%0A%20%20%20%20if%20b%20%3D%3D%201%3A%0A%20%20%20%20%20%20%20%20%23%20%D0%95%D1%81%D0%BB%D0%B8%20%D0%BE%D1%81%D1%82%D0%B0%D1%82%D0%BE%D0%BA%20%D1%80%D0%B0%D0%B2%D0%B5%D0%BD%201%2C%20%D0%BF%D1%80%D0%B5%D0%BE%D0%B1%D1%80%D0%B0%D0%B7%D0%BE%D0%B2%D0%B0%D1%82%D1%8C%20%D0%BE%D0%B4%D0%BD%D1%83%20%D0%BF%D0%B0%D1%80%D1%83%201%20%2A%203%20%D0%B2%202%20%2A%202%0A%20%20%20%20%20%20%20%20return%20int%28math.pow%283%2C%20a%20-%201%29%29%20%2A%202%20%2A%202%0A%20%20%20%20if%20b%20%3D%3D%202%3A%0A%20%20%20%20%20%20%20%20%23%20%D0%95%D1%81%D0%BB%D0%B8%20%D0%BE%D1%81%D1%82%D0%B0%D1%82%D0%BE%D0%BA%20%D1%80%D0%B0%D0%B2%D0%B5%D0%BD%202%2C%20%D0%BD%D0%B8%D1%87%D0%B5%D0%B3%D0%BE%20%D0%BD%D0%B5%20%D0%B4%D0%B5%D0%BB%D0%B0%D1%82%D1%8C%0A%20%20%20%20%20%20%20%20return%20int%28math.pow%283%2C%20a%29%29%20%2A%202%0A%20%20%20%20%23%20%D0%95%D1%81%D0%BB%D0%B8%20%D0%BE%D1%81%D1%82%D0%B0%D1%82%D0%BE%D0%BA%20%D1%80%D0%B0%D0%B2%D0%B5%D0%BD%200%2C%20%D0%BD%D0%B8%D1%87%D0%B5%D0%B3%D0%BE%20%D0%BD%D0%B5%20%D0%B4%D0%B5%D0%BB%D0%B0%D1%82%D1%8C%0A%20%20%20%20return%20int%28math.pow%283%2C%20a%29%29%0A%0A%22%22%22Driver%20Code%22%22%22%0Aif%20__name__%20%3D%3D%20%22__main__%22%3A%0A%20%20%20%20n%20%3D%2058%0A%0A%20%20%20%20%23%20%D0%96%D0%B0%D0%B4%D0%BD%D1%8B%D0%B9%20%D0%B0%D0%BB%D0%B3%D0%BE%D1%80%D0%B8%D1%82%D0%BC%0A%20%20%20%20res%20%3D%20max_product_cutting%28n%29%0A%20%20%20%20print%28f%22%D0%9C%D0%B0%D0%BA%D1%81%D0%B8%D0%BC%D0%B0%D0%BB%D1%8C%D0%BD%D0%BE%D0%B5%20%D0%BF%D1%80%D0%BE%D0%B8%D0%B7%D0%B2%D0%B5%D0%B4%D0%B5%D0%BD%D0%B8%D0%B5%20%D0%BF%D0%BE%D1%81%D0%BB%D0%B5%20%D1%80%D0%B0%D0%B7%D1%80%D0%B5%D0%B7%D0%B0%D0%BD%D0%B8%D1%8F%20%3D%20%7Bres%7D%22%29&codeDivHeight=472&codeDivWidth=350&cumulative=false&curInstr=5&heapPrimitives=nevernest&origin=opt-frontend.js&py=311&rawInputLstJSON=%5B%5D&textReferences=false"> </iframe></div>
|
||||
<div style="margin-top: 5px;"><a href="https://pythontutor.com/iframe-embed.html#code=import%20math%0A%0Adef%20max_product_cutting%28n%3A%20int%29%20-%3E%20int%3A%0A%20%20%20%20%22%22%22%D0%9C%D0%B0%D0%BA%D1%81%D0%B8%D0%BC%D0%B0%D0%BB%D1%8C%D0%BD%D0%BE%D0%B5%20%D0%BF%D1%80%D0%BE%D0%B8%D0%B7%D0%B2%D0%B5%D0%B4%D0%B5%D0%BD%D0%B8%D0%B5%20%D1%80%D0%B0%D0%B7%D1%80%D0%B5%D0%B7%D0%B0%D0%BD%D0%B8%D1%8F%3A%20%D0%B6%D0%B0%D0%B4%D0%BD%D1%8B%D0%B9%20%D0%B0%D0%BB%D0%B3%D0%BE%D1%80%D0%B8%D1%82%D0%BC%22%22%22%0A%20%20%20%20%23%20%D0%9A%D0%BE%D0%B3%D0%B4%D0%B0%20n%20%3C%3D%203%2C%20%D0%BE%D0%B1%D1%8F%D0%B7%D0%B0%D1%82%D0%B5%D0%BB%D1%8C%D0%BD%D0%BE%20%D0%BD%D1%83%D0%B6%D0%BD%D0%BE%20%D0%B2%D1%8B%D0%B4%D0%B5%D0%BB%D0%B8%D1%82%D1%8C%20%D0%BE%D0%B4%D0%BD%D1%83%201%0A%20%20%20%20if%20n%20%3C%3D%203%3A%0A%20%20%20%20%20%20%20%20return%201%20%2A%20%28n%20-%201%29%0A%20%20%20%20%23%20%D0%96%D0%B0%D0%B4%D0%BD%D0%BE%20%D0%B2%D1%8B%D0%B4%D0%B5%D0%BB%D0%B8%D1%82%D1%8C%20%D0%BC%D0%BD%D0%BE%D0%B6%D0%B8%D1%82%D0%B5%D0%BB%D0%B8%203%2C%20%D0%B3%D0%B4%D0%B5%20a%20%E2%80%94%20%D1%87%D0%B8%D1%81%D0%BB%D0%BE%20%D1%82%D1%80%D0%BE%D0%B5%D0%BA%2C%20%D0%B0%20b%20%E2%80%94%20%D0%BE%D1%81%D1%82%D0%B0%D1%82%D0%BE%D0%BA%0A%20%20%20%20a%2C%20b%20%3D%20n%20%2F%2F%203%2C%20n%20%25%203%0A%20%20%20%20if%20b%20%3D%3D%201%3A%0A%20%20%20%20%20%20%20%20%23%20%D0%95%D1%81%D0%BB%D0%B8%20%D0%BE%D1%81%D1%82%D0%B0%D1%82%D0%BE%D0%BA%20%D1%80%D0%B0%D0%B2%D0%B5%D0%BD%201%2C%20%D0%BF%D1%80%D0%B5%D0%BE%D0%B1%D1%80%D0%B0%D0%B7%D0%BE%D0%B2%D0%B0%D1%82%D1%8C%20%D0%BE%D0%B4%D0%BD%D1%83%20%D0%BF%D0%B0%D1%80%D1%83%201%20%2A%203%20%D0%B2%202%20%2A%202%0A%20%20%20%20%20%20%20%20return%20int%28math.pow%283%2C%20a%20-%201%29%29%20%2A%202%20%2A%202%0A%20%20%20%20if%20b%20%3D%3D%202%3A%0A%20%20%20%20%20%20%20%20%23%20%D0%95%D1%81%D0%BB%D0%B8%20%D0%BE%D1%81%D1%82%D0%B0%D1%82%D0%BE%D0%BA%20%D1%80%D0%B0%D0%B2%D0%B5%D0%BD%202%2C%20%D0%BD%D0%B8%D1%87%D0%B5%D0%B3%D0%BE%20%D0%BD%D0%B5%20%D0%B4%D0%B5%D0%BB%D0%B0%D1%82%D1%8C%0A%20%20%20%20%20%20%20%20return%20int%28math.pow%283%2C%20a%29%29%20%2A%202%0A%20%20%20%20%23%20%D0%95%D1%81%D0%BB%D0%B8%20%D0%BE%D1%81%D1%82%D0%B0%D1%82%D0%BE%D0%BA%20%D1%80%D0%B0%D0%B2%D0%B5%D0%BD%200%2C%20%D0%BD%D0%B8%D1%87%D0%B5%D0%B3%D0%BE%20%D0%BD%D0%B5%20%D0%B4%D0%B5%D0%BB%D0%B0%D1%82%D1%8C%0A%20%20%20%20return%20int%28math.pow%283%2C%20a%29%29%0A%0A%22%22%22Driver%20Code%22%22%22%0Aif%20__name__%20%3D%3D%20%22__main__%22%3A%0A%20%20%20%20n%20%3D%2058%0A%0A%20%20%20%20%23%20%D0%96%D0%B0%D0%B4%D0%BD%D1%8B%D0%B9%20%D0%B0%D0%BB%D0%B3%D0%BE%D1%80%D0%B8%D1%82%D0%BC%0A%20%20%20%20res%20%3D%20max_product_cutting%28n%29%0A%20%20%20%20print%28f%22%D0%9C%D0%B0%D0%BA%D1%81%D0%B8%D0%BC%D0%B0%D0%BB%D1%8C%D0%BD%D0%BE%D0%B5%20%D0%BF%D1%80%D0%BE%D0%B8%D0%B7%D0%B2%D0%B5%D0%B4%D0%B5%D0%BD%D0%B8%D0%B5%20%D0%BF%D0%BE%D1%81%D0%BB%D0%B5%20%D1%80%D0%B0%D0%B7%D1%80%D0%B5%D0%B7%D0%B0%D0%BD%D0%B8%D1%8F%20%3D%20%7Bres%7D%22%29&codeDivHeight=800&codeDivWidth=600&cumulative=false&curInstr=5&heapPrimitives=nevernest&origin=opt-frontend.js&py=311&rawInputLstJSON=%5B%5D&textReferences=false" target="_blank" rel="noopener noreferrer">Во весь экран ></a></div>
|
||||
|
||||
{ class="animation-figure" }
|
||||
|
||||
<p align="center"> Рисунок 15-16 Метод вычисления максимального произведения разбиения </p>
|
||||
|
||||
**Временная сложность зависит от того, как в языке программирования реализовано возведение в степень**. Если взять Python, то обычно используются три распространенные функции для вычисления степени.
|
||||
|
||||
- Оператор `**` и функция `pow()` имеют временную сложность $O(\log a)$.
|
||||
- Функция `math.pow()` внутри вызывает функцию `pow()` из библиотеки C, выполняющую возведение в степень с плавающей точкой, и ее временная сложность равна $O(1)$.
|
||||
|
||||
Переменные $a$ и $b$ занимают дополнительную память постоянного размера, **поэтому пространственная сложность равна $O(1)$**.
|
||||
|
||||
### 3. Доказательство корректности
|
||||
|
||||
Используем доказательство от противного и рассмотрим только случай $n \geq 4$.
|
||||
|
||||
1. **Все множители $\leq 3$**: предположим, что в оптимальной схеме разбиения существует множитель $x \geq 4$. Тогда его можно дальше разложить в $2(x-2)$ и получить большее или равное произведение. Это противоречит предположению.
|
||||
2. **Схема разбиения не содержит $1$**: предположим, что в оптимальной схеме присутствует множитель $1$. Тогда его можно объединить с другим множителем и получить большее произведение. Это противоречит предположению.
|
||||
3. **Схема разбиения содержит не более двух $2$**: предположим, что в оптимальной схеме присутствуют три двойки. Тогда их можно заменить двумя тройками и получить большее произведение. Это противоречит предположению.
|
||||
@@ -0,0 +1,18 @@
|
||||
---
|
||||
comments: true
|
||||
---
|
||||
|
||||
# 15.5 Резюме
|
||||
|
||||
### 1. Ключевые моменты
|
||||
|
||||
- Жадный алгоритм обычно используется для решения задач оптимизации. Его принцип состоит в том, чтобы на каждом этапе принятия решения делать локально оптимальный выбор в надежде получить глобально оптимальный ответ.
|
||||
- Жадный алгоритм итеративно делает один жадный выбор за другим, на каждом шаге превращая задачу в подзадачу меньшего размера, пока задача не будет полностью решена.
|
||||
- Жадный алгоритм не только прост в реализации, но и часто обладает высокой эффективностью. По сравнению с динамическим программированием его временная сложность обычно ниже.
|
||||
- В задаче о размене монет для некоторых наборов монет жадный алгоритм способен гарантировать оптимальный ответ, а для других наборов - нет: он может дать очень плохое решение.
|
||||
- Задачи, подходящие для жадного алгоритма, обладают двумя ключевыми свойствами: свойством жадного выбора и оптимальной подструктурой. Свойство жадного выбора отражает корректность жадной стратегии.
|
||||
- Для некоторых сложных задач доказать свойство жадного выбора непросто. Относительно легче найти контрпример и опровергнуть его, как это видно на примере задачи о размене монет.
|
||||
- Решение жадной задачи обычно состоит из трех шагов: анализ задачи, определение жадной стратегии и доказательство корректности. Из них ключевым является выбор жадной стратегии, а доказательство корректности часто оказывается самым трудным.
|
||||
- В задаче о дробном рюкзаке, в отличие от задачи о рюкзаке 0-1, разрешено брать часть предмета, поэтому ее можно решать жадным алгоритмом. Корректность жадной стратегии доказывается методом от противного.
|
||||
- Задачу о максимальной вместимости можно решать полным перебором со временной сложностью $O(n^2)$. Разработав жадную стратегию со сдвигом короткой перегородки внутрь на каждом шаге, временную сложность можно оптимизировать до $O(n)$.
|
||||
- В задаче о максимальном произведении разбиения мы последовательно выводим две жадные стратегии: все целые числа $\geq 4$ следует дальше разбивать, а оптимальным множителем разбиения является $3$. В коде присутствуют операции возведения в степень, поэтому временная сложность зависит от способа их реализации и обычно равна $O(1)$ или $O(\log n)$.
|
||||
Reference in New Issue
Block a user