Краткий ответ: нужно объединить видеоролики каждой темы в отдельную группу и раздавать их видеохостингам по кругу, не сбрасывая указатель между темами.
Алгоритм
- Сохраним номера видеороликов для каждой темы.
- Заведём указатель
curна текущий видеохостинг. - Для каждой темы по очереди:
- берём все её видеоролики;
- отправляем их на хостинги
cur, cur + 1, cur + 2, ...по модулюK; - после каждого ролика сдвигаем
cur.
- Выводим списки роликов для каждого хостинга.
Важно: указатель cur не нужно сбрасывать после обработки темы.
Почему распределение оптимально
Пусть у некоторой темы c видеороликов.
При распределении по кругу каждый видеохостинг получит либо
\[ \left\lfloor \frac{c}{K} \right\rfloor, \]
либо
\[ \left\lceil \frac{c}{K} \right\rceil \]
роликов этой темы. Поэтому разница между максимальным и минимальным количеством равна:
- \(0\), если \(c\) делится на \(K\);
- \(1\), иначе.
Меньшую разницу получить невозможно: если число роликов не делится на число хостингов, одинаковое распределение невозможно.
Все темы обрабатываются подряд, а указатель продолжает двигаться по кругу. Значит, и для всех \(N\) роликов вместе распределение такое же:
\[ \left\lfloor \frac{N}{K} \right\rfloor \quad\text{или}\quad \left\lceil \frac{N}{K} \right\rceil \]
роликов на каждый хостинг. Следовательно, глобальное распределение также оптимально.
Пример
Для примера распределение идёт так:
- тема 1: ролик 1 → хостинг 1;
- тема 2: ролики 2, 3 → хостинги 2, 3;
- тема 3: ролики 4, 5, 6 → хостинги 1, 2, 3;
- тема 4: ролики 7, 8, 9, 10 → хостинги 1, 2, 3, 1.
Получаются строки из примера.
Реализация на C++17
#include <bits/stdc++.h>
using namespace std;
int main() {
ios::sync_with_stdio(false);
cin.tie(nullptr);
int N, M, K;
cin >> N >> M >> K;
vector<vector<int>> byTopic(M + 1);
for (int i = 1; i <= N; ++i) {
int t;
cin >> t;
byTopic[t].push_back(i);
}
vector<vector<int>> hosting(K);
int cur = 0; // текущий хостинг, нумерация с нуля
for (int topic = 1; topic <= M; ++topic) {
for (int video : byTopic[topic]) {
hosting[cur].push_back(video);
cur = (cur + 1) % K;
}
}
for (int host = 0; host < K; ++host) {
cout << hosting[host].size();
for (int video : hosting[host]) {
cout << ' ' << video;
}
cout << '\n';
}
return 0;
}
Сложность алгоритма:
\[ O(N+M+K), \]
поскольку каждый видеоролик обрабатывается ровно один раз.
