printРешение

Тема: перебор, моделирование
Сложность: средняя

В задаче требуется найти такую перестановку `Q_i` чисел от 1 до 7, которая дает минимальный суммарный недосып.

Сначала рассмотрим способы гнерации всех перестановок чисел от 1 на 7. На 1-е место можно поставить любое из 7 чисел, на 2-е – одно из оставшихся 6 чисел, на 3-е – одно из оставшихся 5 чисел, и т.д. Соответствующая часть программы может выглядеть так:
var q:array [1..7] of integer;
...
for q[1]:=1 to 7 do
  for q[2]:=1 to 7 do
    if q[2]<>q[1] then
      for q[3]:=1 to 7 do
        if (q[3]<>q[1]) and (q[3]<>q[2]) then
          for q[4]:=1 to 7 do
             if (q[4]<>q[1]) and (q[4]<>q[2]) and (q[4]<>q[3]) then
               for q[5]:=1 to 7 do
                 if (q[5]<>q[1]) and (q[5]<>q[2]) and 
                    (q[5]<>q[3]) and (q[5]<>q[4]) then
                   for q[6]:=1 to 7 do
                     if (q[6]<>q[1]) and (q[6]<>q[2]) and 
                        (q[6]<>q[3]) and (q[6]<>q[4]) and (q[6]<>q[5]) then
                       for q[6]:=1 to 7 do
                          if (q[7]<>q[1]) and (q[7]<>q[2]) and 
                            (q[7]<>q[3]) and (q[7]<>q[4]) and
                            (q[7]<>q[5]) and (q[7]<>q[6]) then
                            {В массиве q получили перестановку}

Для упрощения программы можно завести массив флажков, в котором отмечать те числа, которые уже используются, и организовать выполнение вложенных циклов с помощью рекурсии:
var q:array [1..7] of integer;
  used:array [1..7] of boolean;
  i:integer;
procedure rec(i:integer);
var j:integer;
begin
  if i>7 then
  begin
    {В массиве q получили перестановку}
    exit;
  end
  for j:=1 to 7 do
    if not used[j] then
    begin
      used[j]:=true;
      q[i]:=j;
      rec(i+1);
      used[j]:=false;
    end;
end;  
...
for i:=1 to 7 do
  used[i]:=false;
rec(1);

Существует немного более сложный, но более эффективный способ генерации перестановок. Генерация начинается с последовательности 1, 2, …, 7. На `i`-м шаге число, стоящее на `i`-м месте меняется с одним из чисел, стоящих на местах с `i`-го по 7-ое и рекурсивно выполняется (`i`+1)-й шаг. По окончании шага нужно восстановить состояние массива на начало шага.
var q:array [1..7] of integer;
  i:integer;
procedure rec(i:integer);
var j,t:integer;
begin
  if i>7 then
  begin
    {В массиве q получили перестановку}
    exit;
  end
  for j:=i to 7 do
  begin
    t:=q[i];
    q[i]:=q[j];
    q[j]:=t;
    rec(i+1);
  end;
  t:=q[i];
  for j:=i+1 to 7 do
    q[i-1]:=q[i];
  q[7]:=t;
end;  
...
for i:=1 to 7 do
  q[i]:=i;
rec(1);

После получения перестановки нужно промоделировать, как будут просыпаться гномы. В массив `S_i` поместим на сколько минут раньше проснется гном, спящий на `i`-м месте.
for i:=1 to 7 do
  s[q[i]]:=c[i]*abs(p[i]-q[i]);
Когда гном просыпается он будит также всех гномов, лежащих ближе к выходу. Гном не может спать дольше гномов, спавших глубже в штреке, поэтому при просмотре массива `S_i` справа налево находим максимум. Суммарное время недосыпа можно вычислить так:
sum:=0;
m:=0;
for i:=7 downto 1 do
begin
  if s[i]>m then
    m:=s[i];
  sum:=sum+m;
end;
Среди сумм недосыпа находим минимум и запоминаем перестановку, при которой он достигается. После обработки всех перестановок выводим перестановку, минимизирующую суммарный недосып.
loading