Матч 346, Быстрый почтальон (FastPostman)

Дивизион 2, Уровень 3

 

Почтальон должен разнести письма по домам, расположенным на одной улице. Адреса представляются собой расстояния от левого конца улицы до домов. Известно граничное время, в течение которого следует доставить каждое письмо в каждый дом. Скорость почтальона 1 метр в секунду, доставка писем производится моментально, как только почтальон поравняется с домом. Необходимо определить, сможет ли почтальон доставить все письма. В случае утвердительного ответа следует найти наименьшее время, за которое он сможет это сделать.

 

Класс: FastPostman

Метод: int minTime(vector<int> address, vector<int> maxTime,

                   int initialPos)

Ограничения: address и maxTime содержат одинаковое количество элементов, не большее 50, 1 £ address[i], initialPos £ 106, 1 £ maxTime[i] £ 109, 1 £ maxTime[i] £ 109.

 

Вход. Массив адресов домов address и массив maxTime, содержащий граничное время разноса соответствующих писем. Переменная initialPos содержит начальное положение почтальона.

 

Выход. Наименьшее время, за которое почтальон сможет разнести все письма по указанным адресам с учетом граничного времени. Если он не сможет это сделать, то вернуть -1.

 

Пример входа

address

maxTime

initialPos

{1,3,5,7}

{9,2,5,100}

4

{1,5}

{6,2}

3

{1000000,1000000,1000000,1000000}

{563,23452,32426,1}

1000000

{1000000}

{45634}

876

 

Пример выхода

13

6

0

-1

 

 

РЕШЕНИЕ

динамическое программирование

 

Если почтальон на своем пути поравняется с некоторым домом, куда следует доставить письмо, то его следует отдавать сразу же, при первом же подходе к дому. Если почтальон уже разнес письма в i - ый и j - ый дома, то он точно уже разнес письма во все дома, которые находятся между i - ым и j - ым домом. Таким образом, множество домов, куда уже разнесена почта, представляет собой отрезок прямой с точкой initialPos внутри. Следует также отметить, что направление движения почтальон может менять только в точке с домом, в который он только что доставил письмо.

Состояние почтальона определим тройкой чисел:

1. левый конец интервала домов, куда уже разнесена почта;

2. правый конец интервала домов, куда уже разнесена почта;

3. текущее положение почтальона;

Вместо самих чисел в состоянии почтальона лучше хранить их индексы в массиве address. На каждом шаге (i, j, k) следует определить, куда лучше двигаться почтальону: вправо (i, j + 1, j + 1) или влево (i – 1, j, i – 1). Также заметим, что для каждого состояния (i, j, k) текущее положение почтальона k равно i или j.

В ячейках sol[i][j][0] будем хранить наименьшее время, за которое почтальон разнесет все письма в дома в интервале (i, j), причем находиться будет в точке i (в левом конце интервала). Соответственно в sol[i][j][1] будет содержаться та же информация, только почтальон будет находиться в правом конце интервала – в точке j.

Отсортируем адреса домов по возрастанию их координат. Чтобы при сортировке соответствующим образом переставлялось и граничное время доставки, создадим вектор пар houses, первый элемент которого содержит адрес дома, а второй – максимальное время доставки письма в этот дом. При сортировке вектора пар по возрастанию сортировка производится по первой компоненте – то есть по расстоянию от левого края улицы.

Инициализация массива sol:

sol[i][i][0] = sol[i][i][1] = | address[i] – initialPos | = | houses[i].first – initialPos |

Для доставки письма в единственный дом, индекс которого равен i, необходимо затратить время, равное расстоянию от начального положения initialPos до местоположения дома address[i].

Рассмотрим интервал (i, j). Почтальон может оказаться в его левом конце двигаясь либо с левого конца интервала (i + 1, j), либо с правого конца интервала (i + 1, j). В первом случае ему требуется пройти расстояние address[i + 1] – address[i], во втором  address[j] –  address[i]. То есть

sol[i][j][0] =

= min(sol[i + 1][j][0] +  address[i + 1] –  address[i], sol[i + 1][j][1] +  address[j] –  address[i])

Аналогично почтальон может оказаться в правом конце итервала (i, j), если он будет двигаться либо с левого конца интервала (i, j – 1), либо с правого конца интервала (i, j – 1). В первом случае ему требуется пройти расстояние address[j] –  address[i], во втором  address[j] –  address[j – 1]. Таким образом

sol[i][j][1] =

= min(sol[i][ j – 1][0] +  address[j] –  address[i], sol[i][ j – 1][1] +  address[j] –  address[j – 1])

Если на каком-нибудь этапе вычисления значение sol[i][j][0] (или sol[i][j][1]) станет большим maxTime[i] (или соответственно maxTime[j]), то установим его равным плюс бесконечности (значению INF = 2000000000). Последнее означает тот факт, что добраться до соответствующего дома в отведенное время почтальон не может.

Почтальон доставит все письма, когда будут пройдены все дома из интервала (0, n – 1), где n – количество домов. При этом нам не важно, в каком из концов этого интервала закончит путь почтальон. То есть ответом будет значение

min(sol[0][n – 1 ][0], sol[0][n – 1][1])

Если это значение равно плюс бесконечности, то разнести письма в срок не удастся и следует вернуть -1 как требуется в условии задачи.

 

ПРОГРАММА

 

#include <cstdio>

#include <vector>

#include <memory>

#include <algorithm>

#define INF 2000000000

using namespace std;

 

int sol[50][50][2];

 

class FastPostman

{

public:

  int minTime(vector<int> address, vector<int> maxTime, int initialPos)

  {

    int res, i, j, n = address.size();

    vector<pair<int,int> > houses;

    for(i = 0; i < n; i++) houses.push_back(make_pair(address[i],maxTime[i]));

    sort(houses.begin(),houses.end());

    memset(sol,0,sizeof(sol));

    for(i = 0; i < n; i++)

    {

      sol[i][i][0] = sol[i][i][1] = abs(houses[i].first - initialPos);

      if (sol[i][i][0] > houses[i].second) sol[i][i][0] = sol[i][i][1] = INF;

    }

    for(i = n - 1; i >= 0; i--)

    {

      for(j = i + 1; j < n; j++)

      {

        sol[i][j][0] = min(sol[i+1][j][0] + houses[i+1].first –

          houses[i].first,sol[i+1][j][1] + houses[j].first - houses[i].first);

        if (sol[i][j][0] > houses[i].second) sol[i][j][0] = INF;

        sol[i][j][1] = min(sol[i][j-1][0] + houses[j].first - houses[i].first,

           sol[i][j-1][1] + houses[j].first - houses[j-1].first);

        if (sol[i][j][1] > houses[j].second) sol[i][j][1] = INF;

      }

    }

    res = min(sol[0][n-1][0],sol[0][n-1][1]);

    if (res == INF) return -1;

    return res;

  }

};