## 🏷️ Задача 1: Оптимальная маршрутизация сборщика заказов ### **Условие:** Сборщик заказов должен собрать товары с полок, расположенных в ряд. Каждая полка имеет координату (позицию) и может содержать один или несколько товаров. Сборщик стартует с позиции 0 и должен посетить все полки с товарами, минимизируя пройденное расстояние. Найдите минимальное расстояние, которое нужно пройти сборщику. ### **Входные данные:** - `positions` - массив позиций полок с товарами (отсортированный) ### **Тестовые примеры:** **Пример 1:** ``` Вход: positions = [2, 3, 10, 15] Выход: 15 Объяснение: Идем от 0 до 15, проходим все полки: 0→2→3→10→15 ``` **Пример 2:** ``` Вход: positions = [1, 9] Выход: 9 Объяснение: 0→1→9 или 0→9→1, оба пути дают расстояние 9 ``` **Пример 3:** ``` Вход: positions = [5] Выход: 5 Объяснение: Просто идем к позиции 5 ``` ### **Расширенные тестовые данные:** ``` Вход: positions = [1, 2, 5, 8, 12, 16, 20, 25, 30, 35] Выход: 35 Объяснение: Оптимальный путь 0→1→2→5→8→12→16→20→25→30→35 ``` ### **Решение:** ```java public int minDistanceToCollectItems(int[] positions) { if (positions == null || positions.length == 0) { return 0; } // Поскольку нужно посетить все позиции и они отсортированы, // оптимально идти от начала до конца return positions[positions.length - 1]; } ``` **Временная сложность:** O(1) **Пространственная сложность:** O(1) ### **Дополнительные вопросы:** 1. Как изменится решение, если сборщик может начать с любой позиции? 2. Что если некоторые полки содержат приоритетные товары, которые нужно собрать в первую очередь? --- ## 📦 Задача 2: Укладка коробок разных размеров ### **Условие:** На складе нужно уложить коробки в стеллаж. Каждая коробка имеет высоту, и стеллаж имеет максимальную высоту H. Коробки можно ставить только одна на другую. Найдите максимальное количество коробок, которые можно уложить в стеллаж. ### **Входные данные:** - `heights` - массив высот коробок - `maxHeight` - максимальная высота стеллажа ### **Тестовые примеры:** **Пример 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: Анализ пиковых нагрузок склада ## Условие: Склад ведет учет количества активных заказов в каждый момент времени. Заказ активен с момента поступления до момента отправки (включая момент поступления, но НЕ включая момент отправки). Найдите максимальное количество одновременно активных заказов. ## Входные данные: 1. `intervals` - массив интервалов `[start, end]`, где каждый интервал представляет время жизни заказа ## Тестовые примеры: ### Пример 1: **Вход:** `intervals = [[1,3],[2,6],[8,10],[15,18]]` **Выход:** `2` **Объяснение:** В момент времени 2 активны заказы [1,3] и [2,6] ### Пример 2: **Вход:** `intervals = [[1,4],[4,5]]` **Выход:** `1` **Объяснение:** Заказы не пересекаются по времени (первый заканчивается в момент 4, второй начинается в момент 4) ### Пример 3: **Вход:** `intervals = [[1,5],[2,3],[4,6],[7,8]]` **Выход:** `2` **Объяснение:** Максимальное пересечение в моменты времени 2 и 4, когда активны по 2 заказа ### Расширенные тестовые данные: **Вход:** `intervals = [[1,10],[2,4],[3,6],[4,8],[5,7],[6,9],[7,12]]` **Выход:** `4` **Объяснение:** Максимальное пересечение в районе времени 5-6, когда активны заказы [1,10], [3,6], [4,8], [5,7] ## Решение: ```java import java.util.*; public int maxConcurrentOrders(int[][] intervals) { if (intervals == null || intervals.length == 0) { return 0; } List events = new ArrayList<>(); // Создаем события начала и конца заказов for (int[] interval : intervals) { events.add(new int[]{interval[0], 1}); // start: +1 events.add(new int[]{interval[1], -1}); // end: -1 } // ИСПРАВЛЕНИЕ: При равном времени сначала обрабатываем начало заказа (+1), потом конец (-1) events.sort((a, b) -> a[0] == b[0] ? b[1] - a[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; } ``` ### Альтернативное решение (JavaScript): ```javascript function maxConcurrentOrders(intervals) { if (!intervals || intervals.length === 0) { return 0; } let events = []; // Создаем события начала и конца заказов for (let interval of intervals) { events.push([interval[0], 1]); // start: +1 events.push([interval[1], -1]); // end: -1 } // При равном времени сначала обрабатываем начало заказа (+1), потом конец (-1) events.sort((a, b) => a[0] === b[0] ? b[1] - a[1] : a[0] - b[0]); let maxConcurrent = 0; let currentConcurrent = 0; for (let event of events) { currentConcurrent += event[1]; maxConcurrent = Math.max(maxConcurrent, currentConcurrent); } return maxConcurrent; } ``` ## Ключевые исправления: 1. **Сортировка событий:** При одинаковом времени сначала обрабатываем события начала заказа (+1), затем события окончания (-1). Это важно, так как заказ считается активным в момент начала, но НЕ активным в момент окончания. 2. **Объяснения к примерам:** Исправлены неточности в объяснениях результатов. 3. **Расширенный пример:** Пересчитан правильный результат. ## Сложность: - **Временная сложность:** O(n log n) - сортировка событий - **Пространственная сложность:** O(n) - список событий ## Дополнительные вопросы: ### 1. Как найти конкретный момент времени, когда достигается максимум? ```java public List findPeakTimes(int[][] intervals) { // ... тот же код до цикла обработки событий List peakTimes = new ArrayList<>(); int maxConcurrent = 0; int currentConcurrent = 0; for (int[] event : events) { currentConcurrent += event[1]; if (currentConcurrent > maxConcurrent) { maxConcurrent = currentConcurrent; peakTimes.clear(); peakTimes.add(event[0]); } else if (currentConcurrent == maxConcurrent && event[1] == 1) { peakTimes.add(event[0]); } } return peakTimes; } ``` ### 2. Как адаптировать алгоритм для поиска минимального количества рабочих станций? Алгоритм остается тем же - максимальное количество одновременно активных заказов и есть минимальное количество рабочих станций, необходимых для обработки всех заказов без задержек. ## Важные моменты: - Заказ активен в интервале `[start, end)` - включая start, но исключая end - При одновременном начале и окончании заказов сначала обрабатываем начало - Алгоритм основан на технике "sweep line" (сканирующая прямая) --- ## 🔍 Задача 5: Поиск товаров в секционированном складе ### **Условие:** Склад разделен на секции, в каждой секции товары отсортированы по ID. Некоторые секции могут быть пустыми. Нужно найти секцию, содержащую товар с заданным ID. ### **Входные данные:** - `sections` - двумерный массив, где каждая строка представляет секцию с отсортированными ID товаров - `target` - искомый ID товара ### **Тестовые примеры:** **Пример 1:** ``` Вход: sections = [[1,4,7,11],[],[2,5,9,10,13],[3,6,8,16]], target = 5 Выход: 2 Объяснение: Товар с ID 5 находится в секции 2 (индексация с 0) ``` **Пример 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 public int searchInSections(int[][] sections, int target) { if (sections == null || sections.length == 0) { return -1; } for (int i = 0; i < sections.length; i++) { if (sections[i].length == 0) { continue; } // Проверяем, может ли target быть в этой секции if (target >= sections[i][0] && target <= sections[i][sections[i].length - 1]) { // Выполняем бинарный поиск в секции if (binarySearch(sections[i], target)) { return i; } } } return -1; } private boolean binarySearch(int[] array, int target) { int left = 0, right = array.length - 1; 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; } } return false; } ``` **Временная сложность:** O(m + log n), где m - количество секций, n - максимальный размер секции **Пространственная сложность:** O(1) ### **Дополнительные вопросы:** 1. Как изменить алгоритм, чтобы найти все секции, содержащие товар? 2. Что если секции не отсортированы и нужно оптимизировать поиск? --- ## 🏃‍♂️ Задача 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