vault backup: 2025-08-13 17:50:19
This commit is contained in:
@@ -1,204 +1,830 @@
|
|||||||
# 🧮 Алгоритмическая задача для Middle разработчика
|
## 🏷️ Задача 1: Оптимальная маршрутизация сборщика заказов
|
||||||
|
|
||||||
## 📝 Задача: Планировщик встреч
|
|
||||||
|
|
||||||
### **Условие:**
|
### **Условие:**
|
||||||
|
|
||||||
У вас есть список встреч, каждая встреча представлена как массив `[start, end]`, где `start` и `end` - время начала и окончания в минутах от начала дня.
|
Сборщик заказов должен собрать товары с полок, расположенных в ряд. Каждая полка имеет координату (позицию) и может содержать один или несколько товаров. Сборщик стартует с позиции 0 и должен посетить все полки с товарами, минимизируя пройденное расстояние.
|
||||||
|
|
||||||
Нужно найти максимальное количество непересекающихся встреч, которые можно провести в одной переговорной комнате.
|
Найдите минимальное расстояние, которое нужно пройти сборщику.
|
||||||
|
|
||||||
### **Примеры:**
|
### **Входные данные:**
|
||||||
|
|
||||||
|
- `positions` - массив позиций полок с товарами (отсортированный)
|
||||||
|
|
||||||
|
### **Тестовые примеры:**
|
||||||
|
|
||||||
**Пример 1:**
|
**Пример 1:**
|
||||||
|
|
||||||
```
|
```
|
||||||
Ввод: meetings = [[0,30],[5,10],[15,20]]
|
Вход: positions = [2, 3, 10, 15]
|
||||||
Вывод: 2
|
Выход: 15
|
||||||
Объяснение: Можно выбрать встречи [5,10] и [15,20]
|
Объяснение: Идем от 0 до 15, проходим все полки: 0→2→3→10→15
|
||||||
```
|
```
|
||||||
|
|
||||||
**Пример 2:**
|
**Пример 2:**
|
||||||
|
|
||||||
```
|
```
|
||||||
Ввод: meetings = [[7,10],[2,4]]
|
Вход: positions = [1, 9]
|
||||||
Вывод: 2
|
Выход: 9
|
||||||
Объяснение: Обе встречи не пересекаются
|
Объяснение: 0→1→9 или 0→9→1, оба пути дают расстояние 9
|
||||||
```
|
```
|
||||||
|
|
||||||
**Пример 3:**
|
**Пример 3:**
|
||||||
|
|
||||||
```
|
```
|
||||||
Ввод: meetings = [[1,5],[8,9],[8,9],[5,9],[9,15]]
|
Вход: positions = [5]
|
||||||
Вывод: 3
|
Выход: 5
|
||||||
Объяснение: Можно выбрать [1,5], [8,9], [9,15]
|
Объяснение: Просто идем к позиции 5
|
||||||
```
|
```
|
||||||
|
|
||||||
### **Ограничения:**
|
### **Расширенные тестовые данные:**
|
||||||
|
|
||||||
- `1 <= meetings.length <= 10^4`
|
```
|
||||||
- `meetings[i].length == 2`
|
Вход: positions = [1, 2, 5, 8, 12, 16, 20, 25, 30, 35]
|
||||||
- `0 <= starti < endi <= 10^6`
|
Выход: 35
|
||||||
|
Объяснение: Оптимальный путь 0→1→2→5→8→12→16→20→25→30→35
|
||||||
|
```
|
||||||
|
|
||||||
### **Задание:**
|
### **Решение:**
|
||||||
|
|
||||||
1. Реализуйте метод `public int maxMeetings(int[][] meetings)`
|
```java
|
||||||
2. Объясните алгоритм и его сложность
|
public int minDistanceToCollectItems(int[] positions) {
|
||||||
3. Напишите unit-тесты
|
if (positions == null || positions.length == 0) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
**Время на выполнение:** 15 минут
|
// Поскольку нужно посетить все позиции и они отсортированы,
|
||||||
|
// оптимально идти от начала до конца
|
||||||
|
return positions[positions.length - 1];
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
**Временная сложность:** O(1)
|
||||||
|
**Пространственная сложность:** O(1)
|
||||||
|
|
||||||
|
### **Дополнительные вопросы:**
|
||||||
|
|
||||||
|
1. Как изменится решение, если сборщик может начать с любой позиции?
|
||||||
|
2. Что если некоторые полки содержат приоритетные товары, которые нужно собрать в первую очередь?
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
# ✅ Решение алгоритмической задачи
|
## 📦 Задача 2: Укладка коробок разных размеров
|
||||||
|
|
||||||
<details> <summary>Показать решение</summary>
|
### **Условие:**
|
||||||
|
|
||||||
### **Основная идея:**
|
На складе нужно уложить коробки в стеллаж. Каждая коробка имеет высоту, и стеллаж имеет максимальную высоту H. Коробки можно ставить только одна на другую. Найдите максимальное количество коробок, которые можно уложить в стеллаж.
|
||||||
|
|
||||||
Это классическая задача о максимальном количестве непересекающихся интервалов. Используем жадный алгоритм:
|
### **Входные данные:**
|
||||||
|
|
||||||
1. Сортируем встречи по времени окончания
|
- `heights` - массив высот коробок
|
||||||
2. Выбираем встречу с самым ранним окончанием
|
- `maxHeight` - максимальная высота стеллажа
|
||||||
3. Исключаем все пересекающиеся с ней встречи
|
|
||||||
4. Повторяем для оставшихся встреч
|
### **Тестовые примеры:**
|
||||||
|
|
||||||
|
**Пример 1:**
|
||||||
|
|
||||||
|
```
|
||||||
|
Вход: heights = [1, 3, 2, 4], maxHeight = 6
|
||||||
|
Выход: 3
|
||||||
|
Объяснение: Можем взять коробки высотой [1, 2, 3] = 6
|
||||||
|
```
|
||||||
|
|
||||||
|
**Пример 2:**
|
||||||
|
|
||||||
|
```
|
||||||
|
Вход: heights = [2, 5, 1, 3], maxHeight = 7
|
||||||
|
Выход: 3
|
||||||
|
Объяснение: [1, 2, 3] = 6 или [1, 3, 2] = 6, но не можем добавить коробку высотой 5
|
||||||
|
```
|
||||||
|
|
||||||
|
**Пример 3:**
|
||||||
|
|
||||||
|
```
|
||||||
|
Вход: heights = [5, 4, 6], maxHeight = 10
|
||||||
|
Выход: 2
|
||||||
|
Объяснение: Максимум можем взять [4, 5] = 9 или [4, 6] = 10
|
||||||
|
```
|
||||||
|
|
||||||
|
### **Расширенные тестовые данные:**
|
||||||
|
|
||||||
|
```
|
||||||
|
Вход: heights = [1, 1, 2, 2, 3, 3, 4, 4, 5, 5], maxHeight = 15
|
||||||
|
Выход: 6
|
||||||
|
Объяснение: [1, 1, 2, 2, 3, 3] = 12 или другие комбинации из 6 коробок
|
||||||
|
```
|
||||||
|
|
||||||
|
### **Решение:**
|
||||||
|
|
||||||
|
```java
|
||||||
|
import java.util.Arrays;
|
||||||
|
|
||||||
|
public int maxBoxes(int[] heights, int maxHeight) {
|
||||||
|
if (heights == null || heights.length == 0) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Сортируем коробки по высоте (жадный подход)
|
||||||
|
Arrays.sort(heights);
|
||||||
|
|
||||||
|
int totalHeight = 0;
|
||||||
|
int count = 0;
|
||||||
|
|
||||||
|
for (int height : heights) {
|
||||||
|
if (totalHeight + height <= maxHeight) {
|
||||||
|
totalHeight += height;
|
||||||
|
count++;
|
||||||
|
} else {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return count;
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
**Временная сложность:** O(n log n) - из-за сортировки
|
||||||
|
**Пространственная сложность:** O(1)
|
||||||
|
|
||||||
|
### **Дополнительные вопросы:**
|
||||||
|
|
||||||
|
1. Как изменится алгоритм, если коробки имеют еще и вес, и есть ограничение по весу?
|
||||||
|
2. Что если коробки нельзя переворачивать и у них есть определенная ориентация?
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 🚛 Задача 3: Планирование загрузки грузовиков
|
||||||
|
|
||||||
|
### **Условие:**
|
||||||
|
|
||||||
|
Склад должен отправить заказы разного веса на грузовиках. Каждый грузовик может везти максимум `capacity` единиц веса. Заказы должны быть отправлены в порядке поступления (нельзя менять порядок). Найдите минимальное количество грузовиков, необходимое для отправки всех заказов.
|
||||||
|
|
||||||
|
### **Входные данные:**
|
||||||
|
|
||||||
|
- `orders` - массив весов заказов
|
||||||
|
- `capacity` - грузоподъемность одного грузовика
|
||||||
|
|
||||||
|
### **Тестовые примеры:**
|
||||||
|
|
||||||
|
**Пример 1:**
|
||||||
|
|
||||||
|
```
|
||||||
|
Вход: orders = [1, 2, 3, 4, 5], capacity = 5
|
||||||
|
Выход: 4
|
||||||
|
Объяснение: [1,2] [3] [4] [5] - 4 грузовика
|
||||||
|
```
|
||||||
|
|
||||||
|
**Пример 2:**
|
||||||
|
|
||||||
|
```
|
||||||
|
Вход: orders = [3, 2, 2, 1], capacity = 6
|
||||||
|
Выход: 2
|
||||||
|
Объяснение: [3,2] [2,1] - 2 грузовика или [3] [2,2,1] - тоже 2
|
||||||
|
```
|
||||||
|
|
||||||
|
**Пример 3:**
|
||||||
|
|
||||||
|
```
|
||||||
|
Вход: orders = [1, 1, 1, 1, 1], capacity = 3
|
||||||
|
Выход: 2
|
||||||
|
Объяснение: [1,1,1] [1,1] - 2 грузовика
|
||||||
|
```
|
||||||
|
|
||||||
|
### **Расширенные тестовые данные:**
|
||||||
|
|
||||||
|
```
|
||||||
|
Вход: orders = [2, 3, 1, 4, 2, 3, 1, 2], capacity = 8
|
||||||
|
Выход: 3
|
||||||
|
Объяснение: [2,3,1] [4,2] [3,1,2] - 3 грузовика
|
||||||
|
```
|
||||||
|
|
||||||
|
### **Решение:**
|
||||||
|
|
||||||
|
```java
|
||||||
|
public int minTrucksNeeded(int[] orders, int capacity) {
|
||||||
|
if (orders == null || orders.length == 0) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
int trucks = 1;
|
||||||
|
int currentLoad = 0;
|
||||||
|
|
||||||
|
for (int order : orders) {
|
||||||
|
if (currentLoad + order <= capacity) {
|
||||||
|
currentLoad += order;
|
||||||
|
} else {
|
||||||
|
// Нужен новый грузовик
|
||||||
|
trucks++;
|
||||||
|
currentLoad = order;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return trucks;
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
**Временная сложность:** O(n)
|
||||||
|
**Пространственная сложность:** O(1)
|
||||||
|
|
||||||
|
### **Дополнительные вопросы:**
|
||||||
|
|
||||||
|
1. Как решить задачу, если можно менять порядок заказов?
|
||||||
|
2. Что если разные грузовики имеют разную грузоподъемность?
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 📊 Задача 4: Анализ пиковых нагрузок склада
|
||||||
|
|
||||||
|
### **Условие:**
|
||||||
|
|
||||||
|
Склад ведет учет количества активных заказов в каждый момент времени. Заказ активен с момента поступления до момента отправки. Найдите максимальное количество одновременно активных заказов.
|
||||||
|
|
||||||
|
### **Входные данные:**
|
||||||
|
|
||||||
|
- `intervals` - массив интервалов `[start, end]`, где каждый интервал представляет время жизни заказа
|
||||||
|
|
||||||
|
### **Тестовые примеры:**
|
||||||
|
|
||||||
|
**Пример 1:**
|
||||||
|
|
||||||
|
```
|
||||||
|
Вход: intervals = [[1,3],[2,6],[8,10],[15,18]]
|
||||||
|
Выход: 2
|
||||||
|
Объяснение: В момент времени 2-3 активны заказы [1,3] и [2,6]
|
||||||
|
```
|
||||||
|
|
||||||
|
**Пример 2:**
|
||||||
|
|
||||||
|
```
|
||||||
|
Вход: intervals = [[1,4],[4,5]]
|
||||||
|
Выход: 1
|
||||||
|
Объяснение: Заказы не пересекаются по времени
|
||||||
|
```
|
||||||
|
|
||||||
|
**Пример 3:**
|
||||||
|
|
||||||
|
```
|
||||||
|
Вход: intervals = [[1,5],[2,3],[4,6],[7,8]]
|
||||||
|
Выход: 3
|
||||||
|
Объяснение: В момент времени 4 активны [1,5], [2,3], [4,6]
|
||||||
|
```
|
||||||
|
|
||||||
|
### **Расширенные тестовые данные:**
|
||||||
|
|
||||||
|
```
|
||||||
|
Вход: intervals = [[1,10],[2,4],[3,6],[4,8],[5,7],[6,9],[7,12]]
|
||||||
|
Выход: 5
|
||||||
|
Объяснение: Максимальное пересечение в районе времени 6-7
|
||||||
|
```
|
||||||
|
|
||||||
### **Решение:**
|
### **Решение:**
|
||||||
|
|
||||||
```java
|
```java
|
||||||
import java.util.*;
|
import java.util.*;
|
||||||
|
|
||||||
public class MeetingScheduler {
|
public int maxConcurrentOrders(int[][] intervals) {
|
||||||
|
if (intervals == null || intervals.length == 0) {
|
||||||
public int maxMeetings(int[][] meetings) {
|
|
||||||
if (meetings == null || meetings.length == 0) {
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Сортируем по времени окончания
|
List<int[]> events = new ArrayList<>();
|
||||||
Arrays.sort(meetings, (a, b) -> Integer.compare(a[1], b[1]));
|
|
||||||
|
|
||||||
int count = 0;
|
// Создаем события начала и конца заказов
|
||||||
int lastEndTime = -1;
|
for (int[] interval : intervals) {
|
||||||
|
events.add(new int[]{interval[0], 1}); // start: +1
|
||||||
for (int[] meeting : meetings) {
|
events.add(new int[]{interval[1], -1}); // end: -1
|
||||||
int startTime = meeting[0];
|
|
||||||
int endTime = meeting[1];
|
|
||||||
|
|
||||||
// Если текущая встреча не пересекается с предыдущей выбранной
|
|
||||||
if (startTime >= lastEndTime) {
|
|
||||||
count++;
|
|
||||||
lastEndTime = endTime;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return count;
|
// Сортируем события по времени, при равном времени сначала -1 (окончание)
|
||||||
|
events.sort((a, b) -> a[0] == b[0] ? a[1] - b[1] : a[0] - b[0]);
|
||||||
|
|
||||||
|
int maxConcurrent = 0;
|
||||||
|
int currentConcurrent = 0;
|
||||||
|
|
||||||
|
for (int[] event : events) {
|
||||||
|
currentConcurrent += event[1];
|
||||||
|
maxConcurrent = Math.max(maxConcurrent, currentConcurrent);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
return maxConcurrent;
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
### **Объяснение алгоритма:**
|
**Временная сложность:** O(n log n) - сортировка событий
|
||||||
|
**Пространственная сложность:** O(n) - список событий
|
||||||
|
|
||||||
1. **Сортировка:** Сортируем все встречи по времени окончания. Это ключевая идея - выбирая встречи с самым ранним окончанием, мы оставляем максимум времени для последующих встреч.
|
### **Дополнительные вопросы:**
|
||||||
|
|
||||||
2. **Жадный выбор:** Проходим по отсортированным встречам и выбираем те, которые не пересекаются с уже выбранными.
|
1. Как найти конкретный момент времени, когда достигается максимум?
|
||||||
|
2. Как адаптировать алгоритм для поиска минимального количества рабочих станций?
|
||||||
|
|
||||||
3. **Условие непересечения:** Встреча не пересекается с предыдущей, если её время начала >= времени окончания предыдущей.
|
---
|
||||||
|
|
||||||
|
## 🔍 Задача 5: Поиск товаров в секционированном складе
|
||||||
|
|
||||||
### **Временная сложность:** O(n log n) - из-за сортировки
|
### **Условие:**
|
||||||
|
|
||||||
### **Пространственная сложность:** O(1) - если не считать пространство для сортировки
|
Склад разделен на секции, в каждой секции товары отсортированы по ID. Некоторые секции могут быть пустыми. Нужно найти секцию, содержащую товар с заданным ID.
|
||||||
|
|
||||||
### **Пошаговый пример:**
|
### **Входные данные:**
|
||||||
|
|
||||||
Для `meetings = [[0,30],[5,10],[15,20]]`:
|
- `sections` - двумерный массив, где каждая строка представляет секцию с отсортированными ID товаров
|
||||||
|
- `target` - искомый ID товара
|
||||||
|
|
||||||
1. После сортировки по времени окончания: `[[5,10], [15,20], [0,30]]`
|
### **Тестовые примеры:**
|
||||||
2. Выбираем `[5,10]`, `lastEndTime = 10`
|
|
||||||
3. Проверяем `[15,20]`: `15 >= 10` ✓, выбираем, `lastEndTime = 20`
|
|
||||||
4. Проверяем `[0,30]`: `0 < 20` ✗, пропускаем
|
|
||||||
5. Результат: 2 встречи
|
|
||||||
|
|
||||||
Для `meetings = [[1,5],[8,9],[8,9],[5,9],[9,15]]`:
|
**Пример 1:**
|
||||||
|
|
||||||
1. После сортировки по времени окончания: `[[1,5], [8,9], [8,9], [5,9], [9,15]]`
|
```
|
||||||
2. Выбираем `[1,5]`, `lastEndTime = 5`
|
Вход: sections = [[1,4,7,11],[],[2,5,9,10,13],[3,6,8,16]], target = 5
|
||||||
3. Проверяем `[8,9]`: `8 >= 5` ✓, выбираем, `lastEndTime = 9`
|
Выход: 2
|
||||||
4. Проверяем `[8,9]`: `8 < 9` ✗, пропускаем
|
Объяснение: Товар с ID 5 находится в секции 2 (индексация с 0)
|
||||||
5. Проверяем `[5,9]`: `5 < 9` ✗, пропускаем
|
```
|
||||||
6. Проверяем `[9,15]`: `9 >= 9` ✓, выбираем
|
|
||||||
7. Результат: 3 встречи
|
|
||||||
|
|
||||||
### **Unit тесты:**
|
**Пример 2:**
|
||||||
|
|
||||||
|
```
|
||||||
|
Вход: sections = [[1,3,5],[2,4,6]], target = 7
|
||||||
|
Выход: -1
|
||||||
|
Объяснение: Товар с ID 7 не найден
|
||||||
|
```
|
||||||
|
|
||||||
|
**Пример 3:**
|
||||||
|
|
||||||
|
```
|
||||||
|
Вход: sections = [[],[1,2,3],[4,5,6]], target = 1
|
||||||
|
Выход: 1
|
||||||
|
Объяснение: Товар с ID 1 находится в секции 1
|
||||||
|
```
|
||||||
|
|
||||||
|
### **Расширенные тестовые данные:**
|
||||||
|
|
||||||
|
```
|
||||||
|
Вход: sections = [[1,5,9,13],[2,6,10,14],[3,7,11,15],[4,8,12,16]], target = 11
|
||||||
|
Выход: 2
|
||||||
|
Объяснение: Товар с ID 11 находится в секции 2
|
||||||
|
```
|
||||||
|
|
||||||
|
### **Решение:**
|
||||||
|
|
||||||
```java
|
```java
|
||||||
import org.junit.jupiter.api.Test;
|
public int searchInSections(int[][] sections, int target) {
|
||||||
import static org.junit.jupiter.api.Assertions.*;
|
if (sections == null || sections.length == 0) {
|
||||||
|
return -1;
|
||||||
class MeetingSchedulerTest {
|
|
||||||
|
|
||||||
private final MeetingScheduler scheduler = new MeetingScheduler();
|
|
||||||
|
|
||||||
@Test
|
|
||||||
void testExample1() {
|
|
||||||
int[][] meetings = {{0,30},{5,10},{15,20}};
|
|
||||||
assertEquals(2, scheduler.maxMeetings(meetings));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
for (int i = 0; i < sections.length; i++) {
|
||||||
void testExample2() {
|
if (sections[i].length == 0) {
|
||||||
int[][] meetings = {{7,10},{2,4}};
|
continue;
|
||||||
assertEquals(2, scheduler.maxMeetings(meetings));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
// Проверяем, может ли target быть в этой секции
|
||||||
void testExample3() {
|
if (target >= sections[i][0] && target <= sections[i][sections[i].length - 1]) {
|
||||||
int[][] meetings = {{1,5},{8,9},{8,9},{5,9},{9,15}};
|
// Выполняем бинарный поиск в секции
|
||||||
assertEquals(3, scheduler.maxMeetings(meetings));
|
if (binarySearch(sections[i], target)) {
|
||||||
|
return i;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
return -1;
|
||||||
void testEmptyInput() {
|
|
||||||
int[][] meetings = {};
|
|
||||||
assertEquals(0, scheduler.maxMeetings(meetings));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
private boolean binarySearch(int[] array, int target) {
|
||||||
void testNullInput() {
|
int left = 0, right = array.length - 1;
|
||||||
assertEquals(0, scheduler.maxMeetings(null));
|
|
||||||
|
while (left <= right) {
|
||||||
|
int mid = left + (right - left) / 2;
|
||||||
|
|
||||||
|
if (array[mid] == target) {
|
||||||
|
return true;
|
||||||
|
} else if (array[mid] < target) {
|
||||||
|
left = mid + 1;
|
||||||
|
} else {
|
||||||
|
right = mid - 1;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
return false;
|
||||||
void testSingleMeeting() {
|
|
||||||
int[][] meetings = {{1,3}};
|
|
||||||
assertEquals(1, scheduler.maxMeetings(meetings));
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
void testAllOverlapping() {
|
|
||||||
int[][] meetings = {{1,4},{2,5},{3,6}};
|
|
||||||
assertEquals(1, scheduler.maxMeetings(meetings));
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
void testNoOverlapping() {
|
|
||||||
int[][] meetings = {{1,2},{3,4},{5,6}};
|
|
||||||
assertEquals(3, scheduler.maxMeetings(meetings));
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
void testTouchingMeetings() {
|
|
||||||
int[][] meetings = {{1,3},{3,5},{5,7}};
|
|
||||||
assertEquals(3, scheduler.maxMeetings(meetings));
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
### **Почему жадный алгоритм работает:**
|
**Временная сложность:** O(m + log n), где m - количество секций, n - максимальный размер секции
|
||||||
|
**Пространственная сложность:** O(1)
|
||||||
|
|
||||||
Выбирая встречу с самым ранним окончанием, мы всегда оставляем максимально возможное время для размещения остальных встреч. Это оптимальная стратегия для данной задачи.
|
### **Дополнительные вопросы:**
|
||||||
|
|
||||||
**Доказательство корректности:** Пусть OPT - оптимальное решение, а GREEDY - решение жадного алгоритма. Можно показать, что GREEDY не хуже OPT путем замены встреч в OPT на встречи из GREEDY без ухудшения результата.
|
1. Как изменить алгоритм, чтобы найти все секции, содержащие товар?
|
||||||
|
2. Что если секции не отсортированы и нужно оптимизировать поиск?
|
||||||
|
|
||||||
</details>
|
---
|
||||||
|
|
||||||
|
## 🏃♂️ Задача 6: Оптимизация маршрута между зонами склада
|
||||||
|
|
||||||
|
### **Условие:**
|
||||||
|
|
||||||
|
Склад имеет зоны, соединенные коридорами. Сотрудник должен пройти через определенные зоны в заданном порядке. Каждый переход между соседними зонами занимает 1 единицу времени. Зоны пронумерованы последовательно. Найдите минимальное время для прохождения всех зон в порядке.
|
||||||
|
|
||||||
|
### **Входные данные:**
|
||||||
|
|
||||||
|
- `zones` - массив номеров зон в порядке посещения
|
||||||
|
|
||||||
|
### **Тестовые примеры:**
|
||||||
|
|
||||||
|
**Пример 1:**
|
||||||
|
|
||||||
|
```
|
||||||
|
Вход: zones = [1, 3, 6, 7]
|
||||||
|
Выход: 6
|
||||||
|
Объяснение: 1→3 (2 шага) + 3→6 (3 шага) + 6→7 (1 шаг) = 6
|
||||||
|
```
|
||||||
|
|
||||||
|
**Пример 2:**
|
||||||
|
|
||||||
|
```
|
||||||
|
Вход: zones = [2, 2, 2]
|
||||||
|
Выход: 0
|
||||||
|
Объяснение: Остаемся в той же зоне, время = 0
|
||||||
|
```
|
||||||
|
|
||||||
|
**Пример 3:**
|
||||||
|
|
||||||
|
```
|
||||||
|
Вход: zones = [10, 5, 8]
|
||||||
|
Выход: 8
|
||||||
|
Объяснение: 10→5 (5 шагов) + 5→8 (3 шага) = 8
|
||||||
|
```
|
||||||
|
|
||||||
|
### **Расширенные тестовые данные:**
|
||||||
|
|
||||||
|
```
|
||||||
|
Вход: zones = [1, 10, 3, 8, 2, 15, 7]
|
||||||
|
Выход: 35
|
||||||
|
Объяснение: |1-10| + |10-3| + |3-8| + |8-2| + |2-15| + |15-7| = 9+7+5+6+13+8 = 48
|
||||||
|
```
|
||||||
|
|
||||||
|
### **Решение:**
|
||||||
|
|
||||||
|
```java
|
||||||
|
public int minTimeToVisitZones(int[] zones) {
|
||||||
|
if (zones == null || zones.length <= 1) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
int totalTime = 0;
|
||||||
|
|
||||||
|
for (int i = 1; i < zones.length; i++) {
|
||||||
|
totalTime += Math.abs(zones[i] - zones[i - 1]);
|
||||||
|
}
|
||||||
|
|
||||||
|
return totalTime;
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
**Временная сложность:** O(n)
|
||||||
|
**Пространственная сложность:** O(1)
|
||||||
|
|
||||||
|
### **Дополнительные вопросы:**
|
||||||
|
|
||||||
|
1. Как решить задачу, если можно изменить порядок посещения зон?
|
||||||
|
2. Что если некоторые переходы между зонами заблокированы?
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 📋 Задача 7: Группировка заказов по приоритету
|
||||||
|
|
||||||
|
### **Условие:**
|
||||||
|
|
||||||
|
На складе есть заказы с разными приоритетами (числа от 1 до k). Заказы одного приоритета должны обрабатываться вместе. Найдите количество групп заказов одинакового приоритета, стоящих подряд.
|
||||||
|
|
||||||
|
### **Входные данные:**
|
||||||
|
|
||||||
|
- `priorities` - массив приоритетов заказов
|
||||||
|
|
||||||
|
### **Тестовые примеры:**
|
||||||
|
|
||||||
|
**Пример 1:**
|
||||||
|
|
||||||
|
```
|
||||||
|
Вход: priorities = [1, 1, 2, 2, 3, 1, 1]
|
||||||
|
Выход: 4
|
||||||
|
Объяснение: Группы: [1,1], [2,2], [3], [1,1]
|
||||||
|
```
|
||||||
|
|
||||||
|
**Пример 2:**
|
||||||
|
|
||||||
|
```
|
||||||
|
Вход: priorities = [1, 2, 1, 2]
|
||||||
|
Выход: 4
|
||||||
|
Объяснение: Группы: [1], [2], [1], [2]
|
||||||
|
```
|
||||||
|
|
||||||
|
**Пример 3:**
|
||||||
|
|
||||||
|
```
|
||||||
|
Вход: priorities = [3, 3, 3, 3]
|
||||||
|
Выход: 1
|
||||||
|
Объяснение: Одна группа: [3,3,3,3]
|
||||||
|
```
|
||||||
|
|
||||||
|
### **Расширенные тестовые данные:**
|
||||||
|
|
||||||
|
```
|
||||||
|
Вход: priorities = [1, 1, 2, 3, 3, 3, 2, 2, 1, 1, 1, 4, 4]
|
||||||
|
Выход: 6
|
||||||
|
Объяснение: [1,1], [2], [3,3,3], [2,2], [1,1,1], [4,4]
|
||||||
|
```
|
||||||
|
|
||||||
|
### **Решение:**
|
||||||
|
|
||||||
|
```java
|
||||||
|
public int countPriorityGroups(int[] priorities) {
|
||||||
|
if (priorities == null || priorities.length == 0) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
int groups = 1;
|
||||||
|
|
||||||
|
for (int i = 1; i < priorities.length; i++) {
|
||||||
|
if (priorities[i] != priorities[i - 1]) {
|
||||||
|
groups++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return groups;
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
**Временная сложность:** O(n)
|
||||||
|
**Пространственная сложность:** O(1)
|
||||||
|
|
||||||
|
### **Дополнительные вопросы:**
|
||||||
|
|
||||||
|
1. Как найти самую длинную группу заказов одного приоритета?
|
||||||
|
2. Что если нужно найти количество различных приоритетов в массиве?
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 🎯 Задача 8: Поиск оптимального места хранения
|
||||||
|
|
||||||
|
### **Условие:**
|
||||||
|
|
||||||
|
На складе есть ряд мест хранения, некоторые заняты (`1`), некоторые свободны (`0`). Нужно найти самую длинную последовательность свободных мест для размещения крупногабаритного товара.
|
||||||
|
|
||||||
|
### **Входные данные:**
|
||||||
|
|
||||||
|
- `storage` - массив состояний мест хранения (0 - свободно, 1 - занято)
|
||||||
|
|
||||||
|
### **Тестовые примеры:**
|
||||||
|
|
||||||
|
**Пример 1:**
|
||||||
|
|
||||||
|
```
|
||||||
|
Вход: storage = [1, 0, 0, 0, 1, 0, 0]
|
||||||
|
Выход: 3
|
||||||
|
Объяснение: Последовательность [0,0,0] имеет длину 3
|
||||||
|
```
|
||||||
|
|
||||||
|
**Пример 2:**
|
||||||
|
|
||||||
|
```
|
||||||
|
Вход: storage = [0, 0, 1, 0, 0, 0, 0, 1]
|
||||||
|
Выход: 4
|
||||||
|
Объяснение: Последовательность [0,0,0,0] имеет длину 4
|
||||||
|
```
|
||||||
|
|
||||||
|
**Пример 3:**
|
||||||
|
|
||||||
|
```
|
||||||
|
Вход: storage = [1, 1, 1, 1]
|
||||||
|
Выход: 0
|
||||||
|
Объяснение: Нет свободных мест
|
||||||
|
```
|
||||||
|
|
||||||
|
### **Расширенные тестовые данные:**
|
||||||
|
|
||||||
|
```
|
||||||
|
Вход: storage = [0, 0, 0, 1, 0, 0, 1, 0, 0, 0, 0, 0, 1, 0]
|
||||||
|
Выход: 5
|
||||||
|
Объяснение: Последовательность [0,0,0,0,0] имеет максимальную длину 5
|
||||||
|
```
|
||||||
|
|
||||||
|
### **Решение:**
|
||||||
|
|
||||||
|
```java
|
||||||
|
public int longestFreeSpace(int[] storage) {
|
||||||
|
if (storage == null || storage.length == 0) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
int maxLength = 0;
|
||||||
|
int currentLength = 0;
|
||||||
|
|
||||||
|
for (int space : storage) {
|
||||||
|
if (space == 0) {
|
||||||
|
currentLength++;
|
||||||
|
maxLength = Math.max(maxLength, currentLength);
|
||||||
|
} else {
|
||||||
|
currentLength = 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return maxLength;
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
**Временная сложность:** O(n)
|
||||||
|
**Пространственная сложность:** O(1)
|
||||||
|
|
||||||
|
### **Дополнительные вопросы:**
|
||||||
|
|
||||||
|
1. Как найти позицию начала самой длинной последовательности?
|
||||||
|
2. Что если можно освободить k занятых мест для увеличения свободного пространства?
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 🔄 Задача 9: Ротация стеллажей для доступа к товарам
|
||||||
|
|
||||||
|
### **Условие:**
|
||||||
|
|
||||||
|
Автоматический стеллаж представляет собой кольцевую структуру с ячейками. Чтобы получить доступ к товару в определенной ячейке, стеллаж можно поворачивать влево или вправо. Каждый поворот на одну позицию занимает 1 единицу времени. Найдите минимальное время для доступа ко всем товарам в заданном порядке.
|
||||||
|
|
||||||
|
### **Входные данные:**
|
||||||
|
|
||||||
|
- `n` - количество ячеек в стеллаже (нумерация 0 до n-1)
|
||||||
|
- `targets` - порядок ячеек для доступа к товарам
|
||||||
|
- `start` - начальная позиция
|
||||||
|
|
||||||
|
### **Тестовые примеры:**
|
||||||
|
|
||||||
|
**Пример 1:**
|
||||||
|
|
||||||
|
```
|
||||||
|
Вход: n = 4, targets = [0, 1, 3], start = 0
|
||||||
|
Выход: 3
|
||||||
|
Объяснение: 0→1 (1 шаг) + 1→3 (2 шага) = 3
|
||||||
|
```
|
||||||
|
|
||||||
|
**Пример 2:**
|
||||||
|
|
||||||
|
```
|
||||||
|
Вход: n = 5, targets = [4, 0, 2], start = 1
|
||||||
|
Выход: 4
|
||||||
|
Объяснение: 1→4 (2 шага) + 4→0 (1 шаг) + 0→2 (2 шага) = 5
|
||||||
|
Проверим: 1→4 влево (3 шага) или вправо (2 шага) - выбираем 2
|
||||||
|
4→0 влево (1 шаг) или вправо (4 шага) - выбираем 1
|
||||||
|
0→2 (2 шага в любую сторону) = 2+1+2 = 5
|
||||||
|
```
|
||||||
|
|
||||||
|
**Пример 3:**
|
||||||
|
|
||||||
|
```
|
||||||
|
Вход: n = 3, targets = [2], start = 1
|
||||||
|
Выход: 1
|
||||||
|
Объяснение: 1→2 (1 шаг в любую сторону)
|
||||||
|
```
|
||||||
|
|
||||||
|
### **Расширенные тестовые данные:**
|
||||||
|
|
||||||
|
```
|
||||||
|
Вход: n = 8, targets = [7, 1, 3, 5], start = 0
|
||||||
|
Выход: 8
|
||||||
|
```
|
||||||
|
|
||||||
|
### **Решение:**
|
||||||
|
|
||||||
|
```java
|
||||||
|
public int minTimeToAccessItems(int n, int[] targets, int start) {
|
||||||
|
if (targets == null || targets.length == 0) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
int totalTime = 0;
|
||||||
|
int currentPos = start;
|
||||||
|
|
||||||
|
for (int target : targets) {
|
||||||
|
int clockwise = (target - currentPos + n) % n;
|
||||||
|
int counterclockwise = (currentPos - target + n) % n;
|
||||||
|
|
||||||
|
totalTime += Math.min(clockwise, counterclockwise);
|
||||||
|
currentPos = target;
|
||||||
|
}
|
||||||
|
|
||||||
|
return totalTime;
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
**Временная сложность:** O(m), где m - количество целевых позиций
|
||||||
|
**Пространственная сложность:** O(1)
|
||||||
|
|
||||||
|
### **Дополнительные вопросы:**
|
||||||
|
|
||||||
|
1. Как изменится решение, если можно изменить порядок доступа к товарам?
|
||||||
|
2. Что если стеллаж может вращаться с разной скоростью в разных направлениях?
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 📈 Задача 10: Анализ производительности конвейера
|
||||||
|
|
||||||
|
### **Условие:**
|
||||||
|
|
||||||
|
На конвейере обрабатываются товары. В каждый момент времени известна скорость конвейера. Найдите период времени, когда средняя скорость была максимальной для окна размером k.
|
||||||
|
|
||||||
|
### **Входные данные:**
|
||||||
|
|
||||||
|
- `speeds` - массив скоростей конвейера в разные моменты времени
|
||||||
|
- `k` - размер временного окна
|
||||||
|
|
||||||
|
### **Тестовые примеры:**
|
||||||
|
|
||||||
|
**Пример 1:**
|
||||||
|
|
||||||
|
```
|
||||||
|
Вход: speeds = [1, 3, 2, 6, 4], k = 3
|
||||||
|
Выход: 2
|
||||||
|
Объяснение: Окна: [1,3,2]=6, [3,2,6]=11, [2,6,4]=12. Максимум в позиции 2
|
||||||
|
```
|
||||||
|
|
||||||
|
**Пример 2:**
|
||||||
|
|
||||||
|
```
|
||||||
|
Вход: speeds = [5, 2, 1, 8, 9], k = 2
|
||||||
|
Выход: 3
|
||||||
|
Объяснение: Окна: [5,2]=7, [2,1]=3, [1,8]=9, [8,9]=17. Максимум в позиции 3
|
||||||
|
```
|
||||||
|
|
||||||
|
**Пример 3:**
|
||||||
|
|
||||||
|
```
|
||||||
|
Вход: speeds = [1, 2, 3, 4, 5], k = 1
|
||||||
|
Выход: 4
|
||||||
|
Объяснение: Максимальная скорость 5 в позиции 4
|
||||||
|
```
|
||||||
|
|
||||||
|
### **Расширенные тестовые данные:**
|
||||||
|
|
||||||
|
```
|
||||||
|
Вход: speeds = [2, 1, 3, 4, 6, 7, 8, 1, 2], k = 4
|
||||||
|
Выход: 4
|
||||||
|
Объяснение: Нужно найти окно размером 4 с максимальной суммой
|
||||||
|
```
|
||||||
|
|
||||||
|
### **Решение:**
|
||||||
|
|
||||||
|
```java
|
||||||
|
public int maxAverageWindow(int[] speeds, int k) {
|
||||||
|
if (speeds == null || speeds.length < k) {
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Вычисляем сумму первого окна
|
||||||
|
int windowSum = 0;
|
||||||
|
for (int i = 0; i < k; i++) {
|
||||||
|
windowSum += speeds[i];
|
||||||
|
}
|
||||||
|
|
||||||
|
int maxSum = windowSum;
|
||||||
|
int maxIndex = 0;
|
||||||
|
|
||||||
|
// Скользящее окно
|
||||||
|
for (int i = k; i < speeds.length; i++) {
|
||||||
|
windowSum = windowSum - speeds[i - k] + speeds[i];
|
||||||
|
|
||||||
|
if (windowSum > maxSum) {
|
||||||
|
maxSum = windowSum;
|
||||||
|
maxIndex = i - k + 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return maxIndex;
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
**Временная сложность:** O(n)
|
||||||
|
**Пространственная сложность:** O(1)
|
||||||
|
|
||||||
|
### **Дополнительные вопросы:**
|
||||||
|
|
||||||
|
1. Как найти все окна с максимальной средней скоростью?
|
||||||
|
2. Что если нужно найти окно с минимальной дисперсией скоростей?
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 📝 Инструкции по использованию
|
||||||
|
|
||||||
|
### **Для интервьюера:**
|
||||||
|
|
||||||
|
1. **Выбор задач:** Для Middle разработчика рекомендуется 2-3 задачи на 30-45 минут
|
||||||
|
|
||||||
|
2. **Оценка:** Обращайте внимание на:
|
||||||
|
|
||||||
|
- Правильность алгоритма
|
||||||
|
- Оптимальность решения
|
||||||
|
- Качество кода
|
||||||
|
- Обработку граничных случаев
|
||||||
|
- Ответы на дополнительные вопросы
|
||||||
|
3. **Подсказки:** Если кандидат затрудняется, можно дать наводящие вопросы по теме складской логистики
|
||||||
|
|
||||||
|
|
||||||
|
### **Уровни сложности:**
|
||||||
|
|
||||||
|
- **Easy:** Задачи 1
|
||||||
Reference in New Issue
Block a user