Перейти до основного вмісту

Оптимальний розклад робіт за їхніми дедлайнами та тривалостями

Нехай у нас є набір робіт, і для кожної роботи ми знаємо її дедлайн та тривалість. Виконання роботи не можна перервати до її завершення. Потрібно скласти такий розклад, щоб виконати якнайбільше робіт.

Коли підходить цей алгоритм?
  • Чи кожна робота задається парою (дедлайн, тривалість) і мета — максимізувати кількість виконаних робіт?
  • Чи всі роботи виконуються на одній машині й допустиме розбиття часу на відрізки між сусідніми дедлайнами?
  • Чи штрафу за час очікування немає — інакше це задача про мінімізацію штрафу → Планування робіт на одній машині?

Розв'язок

Алгоритм розв'язання — жадібний. Відсортуймо всі роботи за їхніми дедлайнами та переглядаймо їх у порядку спадання. Також створімо чергу qq, у яку поступово додаватимемо роботи й діставатимемо ту, що має найменший час виконання (наприклад, можна використати set або priority_queue). Спочатку qq порожня.

Нехай ми розглядаємо ii-ту роботу. Насамперед додаймо її до qq. Розгляньмо проміжок часу між дедлайном ii-ї роботи та дедлайном i1i-1-ї роботи. Це відрізок деякої довжини TT. Діставатимемо роботи з qq (у порядку зростання їхньої залишкової тривалості) і виконуватимемо їх, поки весь відрізок TT не заповниться. Важливо: якщо в якийсь момент дістану роботу можна виконати лише частково до заповнення відрізка TT, то виконуємо цю роботу частково настільки, наскільки це можливо, тобто протягом часу TT, а залишкову частину роботи кладемо назад у qq.

Після завершення роботи алгоритму ми отримаємо оптимальний розв'язок (або принаймні один з кількох розв'язків). Час роботи алгоритму — O(nlogn)O(n \log n).

Реалізація

Наведена нижче функція приймає вектор робіт (кожна з яких складається з дедлайну, тривалості та індексу роботи) і обчислює вектор, що містить усі індекси використаних робіт в оптимальному розкладі. Зверніть увагу, що вам усе ще потрібно відсортувати ці роботи за їхнім дедлайном, якщо ви хочете явно записати план.

struct Job {
int deadline, duration, idx;

bool operator<(Job o) const {
return deadline < o.deadline;
}
};

vector<int> compute_schedule(vector<Job> jobs) {
sort(jobs.begin(), jobs.end());

set<pair<int,int>> s;
vector<int> schedule;
for (int i = jobs.size()-1; i >= 0; i--) {
int t = jobs[i].deadline - (i ? jobs[i-1].deadline : 0);
s.insert(make_pair(jobs[i].duration, jobs[i].idx));
while (t && !s.empty()) {
auto it = s.begin();
if (it->first <= t) {
t -= it->first;
schedule.push_back(it->second);
} else {
s.insert(make_pair(it->first - t, it->second));
t = 0;
}
s.erase(it);
}
}
return schedule;
}

Відеоматеріали