Шаг 72.
Алгоритмы.
Решение задачи "Кубики"

    На этом шаге рассмотрим решение задачи "Кубики".

    Составим для данной задачи граф следующим образом: в качестве базовых вершин будем рассматривать заданные кубики, а также вершины, соответствующие буквам, которые могут быть написаны на заданных кубиках 26 заглавных букв латинского алфавита).

    Добавим также вершину-исток (ее соединим со всеми вершинами, соответствующими кубиками) и вершину-сток (с ней соединим все вершины, соответствующие буквам).

    Фрагменты программы, которые обеспечат необходимый ввод исходных данных и построение графа, приведены ниже:

readln (N); {Количество кубиков} 
readln (Name); {Имя Петиной сестры}  
for i := 1  to  N do readln(Qubics[i]); {Надписи на кубиках}  
Finish := N + 26  + 1 ; {Кубики + алфавит + сток} 

    Поскольку с каждого кубика может быть использована только одна буква, то веса дуг от истока к кубикам равны 1.

{Добавляем вершину-исток} 
ka[0] := N; {Из истока - N дуг к каждому кубику} 
for i := l to N do 
begin 
   a[0,i] := i; {Номер вершины, соединенной с истоком} 
   c[0,i] := l; {вес соответствующей дуги} 
end; 

    Фрагмент графа от вершин, соответствующих заглавным буквам латинского алфавита, к стоку определяется именем Петиной сестры (переменная Name), которое по условиям задачи требуется составить из исходных кубиков:

{Добавляем вершину-сток} 
for i := 1 to Length(Name) do 
begin 
   NomLet := Ord(Name[i]) - Ord('A') + 1; 
   ka[N + NomLet] := 1; 
   a[N + NomLet,1] := Finish; {Одна дуга на каждую букву из имени} 
   inc(c[N + NomLet, Finish]); {ee вес - количество таких букв в имени}
end; 

    Поскольку имя Петиной сестры может включать повторяющиеся буквы, то при формировании веса соответствующей дуги используется инкрементирование — inc(c[N + NomLet, Finish]).

    Наконец для каждой буквы на каждом кубике строится дуга от соответствующего кубика к соответствующей букве.

    Заметим, что на одном кубике буквы могут повторяться, и мы можем для таких дуг либо увеличивать их вес, либо добавлять новую дугу (с весом 1 — именно такой вариант и реализован в приведенном фрагменте программы). Алгоритм построения максимального потока работает и в графах, содержащих более одной дуги между одной и той же парой вершин.

{Добавляем дуги от кубиков к буквам} 
for i := 1  to N do {Для каждого кубика}  
begin 
   for j := 1  to 6  do {Для каждой буквы на кубике}  
   begin 
      NomLet := Ord(Qubics[i,j]) - Ord('A') + 1; {Вычисляем номер буквы}  
      a[i,j] := N + NomLet; {Добавляем дугу от кубика к букве}  
      c[i, a[i,j]] := 1; {Bec такой дуги - 1}  
      NomKub[Nomlet] := 1; 
      inc(ka[i]) 
   end;
end; 

    Чтобы получить ответ на вопрос, поставленный в задаче ("Выясните, можно ли выложить ее имя с помощью этих кубиков и если да, то в каком порядке следует выложить кубики"), после построения максимального потока назаданном графе следует высчитать максимальный исходящий поток Мах:

Max := 0; 
for i := N + 1 to N + 26 do Max := Max + f[i, Finish];

    Если значение Мах не равно длине имени Name, то ответ отрицательный (N0), иначе — положительный (YES). Для того чтобы вывести номера использованных кубиков в порядке, обеспечивающем построение имени, нужно для каждой буквы имени (NomLet) найти такую вершину k в графе (и вывести ее номер), что f[k, Nomlet] = 1. Фрагмент программы, выводящий ответ задачи, приводится ниже:

If Max <> Length(Name) then writeln('NO') 
else 
begin 
     writeln('YES'); 
     for i := 1 to Length(Name) do 
     begin 
         NomLet := Ord(Name[i]) - Ord('A') + 1; {Вычисляем номер буквы} 
         for k := 1 to n do 
             if f[k, N + NomLet] = 1 then 
             begin 
		 write(k,' '); 
                 f[k, N + NomLet] := 0; 
                 break; 
             end; 
    end;
end;

    Архив с примером можно взять здесь.

    На следующем шаге рассмотрим постановку еще одной олимпиадной задачи.




Предыдущий шаг Содержание Следующий шаг