Конфуз(#1053) Кубики(#1055)
#1054

Задача g6_1054: Автобусный диспетчер (XIV Всероссийская олимпиада по информатике)
На кольцевом маршруте №54 протяженностью S, проходящем мимо пансионата "Энергетик", работает N автобусов. Автобусы пронумерованы числами от 1 до N в порядке их следования по маршруту. Автобус с номером 1 движется за автобусом с номером N. Расписание составлено таким образом, что автобусы движутся с одинаковой скоростью V0 и с равными интервалами между ними. Движение автобусов контролирует диспетчер.
В 12 часов дня некоторые K автобусов одновременно снимаются с маршрута и отправляются на обед. Для восстановления равенства интервалов между автобусами, продолжающими движение по маршруту, потребуется некоторое время Т и, возможно, изменение скорости некоторых автобусов по команде диспетчера. В течение этого времени автобусы должны двигаться с постоянными скоростями из интервала [Vmin, Vmax], назначенными диспетчером. Изменение скорости движения автобуса происходит мгновенно. По истечении времени Т автобусы возобновляют движение по маршруту со скоростью V0.
Требуется написать программу для автоматического диспетчера, которая вычисляет минимальное время Tmin, за которое интервалы движения между оставшимися автобусами станут равными, и скорости движения каждого из них в течение этого времени.
Входные данные
Входной файл bus.in содержит две строки.
В первой строке находятся натуральные числа N, К, S, Vmin, Vmax и V0, где K < N <= 10000, S <= 10000, Vmin < Vmax <= 10000, Vmin <= V0 <= Vmax.
Во второй строке расположены в порядке возрастания K чисел - номера автобусов, снятых с маршрута. Все данные в строках разделены пробелами.
Выходные данные
В первой строке выходного файла bus.out должно находиться значение Tmin.
В каждой из последующих N - K строк должны быть по два разделенных пробелом числа - номер автобуса на маршруте и скорость его движения в течение времени Tmin. Номера автобусов упорядочить по возрастанию. Значения Tmin и скоростей выводить с точностью до 4-х значащих цифр после десятичной точки.
Пример 1
Входной файл bus.in
4 1 60 21 70 60
3
Выходной файл bus.out
0.2041
1 45.5
2 21
4 70
Пример 2
Входной файл bus.in
4 2 40 30 80 50
2 4
Выходной файл bus.out
0
1 50
3 50
Решение g6_1054: предоставлено С.В. Густокашиным
При решении этой задачи не обошлось без пеньков, о которые я споткнулся, ну а число этих пеньков, как обычно - роковое: три.
Начну со второго, как наиболее крупного и наиболее часто встречающегося. Когда программа уже отлажена и начинаю сверять решение с контрольным примером - числа сходятся, но их порядок другой. Недоумение, поиск ошибок в алгоритме - ничего.
Читаю задачу близким, убеждаю их, что ответы должны идти именно в таком порядке, а иначе автобусы должны ехать задом наперед и тут при очередном прочтении условия осеняет - О, Боже!
Они действительно едут не первый за вторым, второй за третьим и т.д., а второй за первым, третий за вторым и т.д. А это значит мною была невыполнена первая заповедь: внимательно читать условие задачи и не додумывать того, чего нет. Да и рисунки делать только сверяясь с каждым предложением задачи, я ведь нарисовал колечко и написал номера автобусов, только номера-то надо было подписывать согласуясь с условиями, а не просто используя стандартный числовой ряд - 1, 2, 3, 4. Теперь сам разбор и по ходу об остальных "граблях".
Из чего исходим: автобус, проезжающий за время TMin самый длинный путь должен иметь скорость VMax. Значит определив самый длинный путь и имея VMax из условия задачи можно рассчитать TMin. Ну, а имея время и пройденные пути за это время для других автобусов, определить скорости не составит труда.
До определения пройденных путей за время TMin сначала определим расстояния между автобусами после снятия некоторых из них с маршрута. Заодно совместим эту процедуру с запоминанием номеров оставшихся автобусов:

      J:= N - K + 1;
      for I:= N downto 1 do
        if M[I] = 0 then inc(MDist[J])
        else begin dec(J); MDist[J]:= 1; MNum[J]:= I; end;
      MDist[1]:= MDist[1] + MDist[N - K + 1];

Необходимо отметить, что расстояние между оставшимися автобусами MDist[J] рассчитываем как число первоначальных интервалов между автобусами. Это первый пенек, о который я споткнулся. Сначала, не мало сумняшись, был объявлен массив MDist: array[1..10000] of real; и расчет реального расстояния, возникшего между автобусами. Понятно, что Turbo Pascal 7.0 такого громадного массива вкупе с другими (M[I] и MNum[J]) не потянул, потому появилось объявление MDist: array[1..10000] of integer; и подсчет расстояния в виде числа первоначальных интервалов между автобусами. (этой проблемы можно избежать, если использовать компилятор FreePascal)
Далее привожу текст расчета минимального времени.
Исходим из того, что первый автобус неподвижен (едет с минимальной скоростью), и рассчитываем пути пройденные остальными автобусами, запоминая максимальный и минимальный, чтобы в конце по полученным значениям рассчитать время.

      MaxWeg:= 0; MinWeg:= 0; D:= 0; Flag:= false; T:= 0; VVV:= V0;
      for I:= 2 to N - K do begin
        Work:= MDist[I] * FInt - LInt + D;
        if abs(Work) < 5.E-10 then Work:= 0;
        if Work <> MaxWeg then Flag:= true;
        if Work > MaxWeg then MaxWeg:= Work;
        if Work < MinWeg then MinWeg:= Work;
        D:= Work;
      end;

Теперь определяем время и скорость первого автобуса.

      if Flag then begin
        T:= (MaxWeg - MinWeg)/(VMax - VMin); VVV:= -MinWeg/T + VMin;
      end;
      writeln(Out, T:1:4); writeln(Out, MNum[1], ' ', VVV:1:4);

Последнее усилие:

      D:= 0;
      for I:= 2 to N - K do begin
        Work:= MDist[I] * FInt - LInt + D;
        if Flag then VVV:= (Work - MinWeg)/T + VMin;
        writeln(Out, MNum[I], ' ', VVV:1:4);
        D:=Work;
      end;

И на десерт разбор последней ошибки, которая нашлась, что очень огорчило, только при прогоне имеющихся тестов. В результате в расчет максимального и минимального пути была добавлена строка

      if abs(Work) < 5.E-10 then Work:= 0;

В чем суть: когда сравниваются два числа, из которых хотя бы одно расчетное, нужно производить округление. В данном случае точность выбрана произвольно, хотя я думаю неплохо бы ее обосновать.
Скачать тесты к задаче
Наверх