9.2. Демонстрация. Задача о составлении расписания
Шаг 1
Задача о составлении расписания в различных её формулировках является ещё одной популярной задачей из серии решаемых жадно. Давайте посмотрим на неё и решим с помощью жадности! В вашем отделе внезапно уволились два из трёх менеджеров, и все их встречи и организационные вопросы перешли к третьему. Конечно, на все встречи один менеджер попасть не может — они просто накладываются по времени друг на друга. Помогите менеджеру организовать свой календарь таким образом, чтобы попасть на максимальное количество встреч.
import {timetable} from "./logger";
const appointments = [
{
title: "Планирование и приоритизация задач",
start: 10,
end: 13,
},
{
title: "Обед",
start: 12,
end: 13,
},
{
title: "О защите от мошенников",
start: 10,
end: 11,
},
{
title: "Об увольнении Игоря",
start: 14,
end: 16,
},
{
title: "Встреча, которую можно было бы заменить одним письмом",
start: 13,
end: 15,
},
{
title: "Что подарим Кексу на день рождения?",
start: 15,
end: 17,
},
{
title: "Вопросы с поставками",
start: 9,
end: 11,
},
];
function prioritize(appointments) {
return appointments;
}
timetable(prioritize(appointments));
Шаг 2
Решение у этой задачи невероятно простое. Сначала нам нужно найти встречу, которая заканчивается раньше остальных. После того как мы её найдём, нужно найти встречу, которая начинается после первой и заканчивается также самой первой. По такому принципу составим расписание, пока есть встречи, которые в него «помещаются». Кажется, что такой алгоритм настолько прост, что должен работать не всегда. Но нет, в этом и есть прелесть жадных алгоритмов: иногда они так просты, что даже выглядят глупо.
import {timetable} from "./logger";
const appointments = [
{
title: "Планирование и приоритизация задач",
start: 10,
end: 13,
},
{
title: "Обед",
start: 12,
end: 13,
},
{
title: "Вопросы с поставками",
start: 9,
end: 11,
},
{
title: "Об увольнении Игоря",
start: 14,
end: 16,
},
{
title: "Встреча, которую можно было бы заменить одним письмом",
start: 13,
end: 15,
},
{
title: "Что подарим Кексу на день рождения?",
start: 15,
end: 17,
},
{
title: "О защите от мошенников",
start: 10,
end: 11,
},
];
function prioritize(appointments) {
const timetable = [];
// Сохраним здесь время начала самой поздней встречи, чтобы остановить алгоритм, когда уже перейдем это время
const {end: latestAppointment} = appointments.reduce((currentLatest, next) => currentLatest.end < next.end ? next : currentLatest, appointments[0]);
// Будем хранить здесь время окончания предыдущей встречи
let startAfter = 0;
// пока мы ещё можем впихнуть встречу в конец
while (startAfter < latestAppointment) {
// Найдем встречу, которая заканчивается максимально рано после последней и вставим её в конец!
const nextAppointments = appointments.filter(({start}) => start >= startAfter);
const appointment = nextAppointments
.reduce((currentShortest, next) => currentShortest.end > next.end ? next : currentShortest, nextAppointments[0]);
startAfter = appointment.end;
timetable.push(appointment);
}
return timetable;
}
timetable(prioritize(appointments));
File logger.js
const timetableElement = document.getElementById('results');
export function timetable(appointments) {
for (const {title, start, end} of appointments) {
const appt = document.createElement('li');
appt.innerText = `${title}, с ${start} до ${end}`;
timetableElement.appendChild(appt);
}
}