Добавил:
Upload Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:
delphi / песни о паскале.pdf
Скачиваний:
62
Добавлен:
26.03.2016
Размер:
5.16 Mб
Скачать

Глава 49 Сложные массивы

Элементом массива может быть любой тип данных, и даже другой массив. Разбор следующих задач убедит вас в этом.

На поклон к Науке

Вернемся в тридевятое царство, история которого ещё далека от завершения. В 38-й главе мы узнали, что для исследования материка запустили спутник, передавший на землю номера границ тамошних стран. А программа, сработанная придворным программистом Ником, нашла по этим данным соседей царства А.

Молва о научном успехе дошла до купцов, и заронила в их души зерно надежды. Душевной болью торгашей были немалые пошлины, вносимые при каждом пересечении границы. Нет, купцы не надеялись избавиться от пошлин. Но, прежде чем везти товар, они желали знать о количестве пересекаемых границ, дабы прикинуть, стоит ли овчинка выделки? Раньше купцы ехали, куда глаза глядят и часто терпели убытки. Но теперь — иное дело, — можно предвидеть расходы. Требовалась лишь программа для определения минимального количества пересекаемых границ. С этой просьбой купцы и подкатили к придворному программисту, обещая весомое вознаграждение.

Ник выслушал купцов, и представил себе задачу так. Есть файл с номерами границ каждого государства, а также названия двух стран, назовем их условно А и Б. Надо вычислить наименьшее число пересекаемых границ на пути из страны А в страну Б. Напоминаю, что переходить границы в углах не разрешалось, поэтому соприкосновение стран углами не считается общей границей.

Имперское строительство

Ник принял заказ и погрузился в работу. Однако щедрые посулы купцов не содействовали раздумьям, — вдохновение не являлось, хоть убей! В таких случаях Ник пытался отвлечься; вот и сейчас его рука потянулась к полке и достала первую попавшуюся книгу — это была история средних веков. Книга открылась на странице с рассказом о зарождении средневековой империи. Парень увлекся чтением, забыв на время о своих неудачах. Но вскоре Ника осенило: «Ведь это то, что мне нужно, — блеснуло в его голове, — я должен построить империю!»

Вам приходилось строить империи? Тогда послушайте меня — опытного «императора». Строительство начинается с собственной страны — центра империи. Я готовлю мощную армию и накапливаю прочие ресурсы: оружие, горючее, продовольствие. Всё это нужно для «добровольного» присоединения соседей. Затем нападаю на них и покоряю поодиночке. После такой завоевательной кампании рождается новая страна с расширенными границами и другими соседями, которые ещё не ведают о своей судьбе! Дав отдых армии, и накопив ресурсы, я предпринимаю следующую завоевательную кампанию и

377

Глава 49

Сложные массивы

присоединяю соседей своих бывших соседей. Взгляните на рис. 112, — если строительство империи начать из страны D, то в ходе первой кампании будут поглощены соседи A, C и E, а в ходе второй — их соседи, страны B, I и F.

14

13

12

1

 

 

2

3

 

 

 

 

 

 

 

 

 

H

 

17

 

18

4

 

 

 

 

 

 

 

 

 

 

 

16

 

 

28

B

 

 

 

 

 

 

 

15

 

I

32

29

19

C

 

 

 

 

 

 

 

 

 

 

 

 

 

 

27

 

 

 

 

20

5

 

 

31

A

G

 

 

 

 

 

 

 

 

 

 

 

 

21

 

 

26

 

 

30

 

D

6

 

 

 

 

 

 

F

23

 

 

 

 

 

 

22

 

 

 

 

 

 

 

 

 

 

 

24

E

7

 

 

 

 

 

 

 

11

 

 

25

 

 

 

 

 

 

 

 

 

 

10

 

9

8

 

 

 

 

 

 

 

 

-исходная страна центр «империи» (D)

-первая завоевательная кампания (A, C, E)

-вторая завоевательная кампания (B, I, F)

Рис. 112 – Строительство империи

Хорошо, — скажете, — но где тут связь с купеческим заказом? Сейчас объясню. Остроумный Ник догадался, что каждая завоевательная кампания уменьшает число границ между центром империи и любой другой, пока ещё независимой, страной ровно на единицу. И так будет, пока страна не поглотится империей, и граница между ними исчезнет. Стало быть, количество необходимых для поглощения страны завоевательных кампаний будет равно количеству пересечений границ, которое интересует купцов.

378

Глава 49

Сложные массивы

«Нашел, нашел!» — просветлел Ник, подвигаясь к компьютеру. Главная идея родилась, осталось обдумать детали. В первую очередь, следовало выбрать подходящий способ хранения границ. В 38-й главе для этого использовано несколько переменных-множеств. Теперь так не годится, — догадался Ник, — ведь для каждой завоевательной кампании мне надо организовать цикл. А там где циклы, там и массивы. Стало быть, мне нужен массив множеств. Ник нарек этот тип данных именем TStates.

type

TBoundSet = set of 1..255;

{ множество границ одной страны }

 

TStates = array ['A'..'Z'] of TBoundSet; { массив из множеств }

var

States : TStates;

{ Переменная-массив }

 

 

 

 

Вы помните, как именовались страны в тех местах? Буквами латинского алфавита. Это надоумило Ника индексировать массив именно символами, — ведь это один из перечислимых типов, а все они пригодны для индексации. Тогда множество границ страны B, имя которой хранится в символьной переменной X, извлекается из массива множеств States так.

var

B : TBoundSet;

{ множество границ одной страны }

...

B:= States[X];

{ здесь X = ’A’...’Z’ – символ-название страны }

 

 

 

 

Но как в таком случае перебирать элементы массива? Ведь к символу не прибавишь единицу! Спасает функция Succ. Напомню, что она возвращает следующее по порядку значение перечислимого типа, например:

X:= ’A’;

 

X:= Succ(X);

{ X = ’B’ }

X:= Succ(X);

{ X = ’C’ }

 

 

Ещё один подводный камень, вовремя подмеченный Ником, был таков. При вводе имени несуществующей страны программа зациклится, вращаясь в замкнутом круге. Потому при вводе данных организован упрямый цикл REPEATUNTIL, вынуждающий пользователя ввести правильные названия стран.

И, наконец, последнее замечание к программе P_49_1 касается переменной Temp (что значит «временная»). Поскольку текущие границы империи накапливаются в переменной EmpireB и расширяются в ходе кампании, то определять бывших соседей по этим границам нельзя! Поэтому предыдущие границы империи перед началом цикла запоминаются в переменной Temp.

Temp:= EmpireB; { Запоминаем границы империи до начала кампании }

Теперь рассмотрите программу P_49_1, затем испытайте её.

379

Глава 49

Сложные массивы

{ P_49_1 – Решение «купеческой» задачи о пересечении границ }

type TNameRange= 'A'..'Z';

{

Диапазон возможных названий стран }

TNameSet = set of TNameRange; {

Множество названий стран }

TBoundSet = set of 1..255;

{

множество границ некоторой страны }

{Массив множеств TStates – это границы всех стран } TStates = array ['A'..'Z'] of TBoundSet;

{Процедура чтения множества чисел (границ) из строки файла } procedure ReadSet(var aFile: text; var aSet : TBoundSet);

var k : integer; begin

aSet:= [];

while not Eoln(aFile) do begin Read(aFile, k);

aSet:= aSet+[k];

end;

Readln (aFile);

end;

{Глобальные переменные }

var

FileIn

: text;

{ Входной файл, полученный со спутника }

 

States

: TStates;

{ Массив множеств границ }

 

Names

: TNameSet;

{ Множество имен всех стран на карте }

 

C1, C2

: char;

{ Имена стран "откуда" и "куда" }

 

C

: char;

{ Рабочая переменная для имен стран }

 

EmpireB: TBoundSet;

{ Границы империи }

 

Temp

: TBoundSet;

{ Предыдущие границы империи }

 

EmpireN: TNameSet;

{ страны империи }

 

Counter: integer;

{ Счетчик пересечений границ (результат) }

begin

{--- Главная программа ---}

{ Открываем входной файл }

 

Assign(FileIn, 'P_38_3.in'); Reset(FileIn);

{ Готовим цикл чтения массива множеств } Names:=[ ]; { Названия стран }

C:= 'A'; { Первый индекс в массиве стран }

380

Глава 49

Сложные массивы

while not Eof(FileIn) do begin

{ Цикл чтения массива множеств }

ReadSet(FileIn, States[C]);

{ Чтение одного множества }

Names:= Names+[C];

{ Добавляем имя страны }

C:= Succ(C);

{ Переход к следующей букве }

end;

Close(FileIn); { Теперь входной файл можно закрыть } repeat { «Упрямый» цикл чтения правильных имен стран }

Write('Откуда: '); Readln(C1);

Write('Куда : '); Readln(C2);

{ Переводим имена стран в верхний регистр }

C1:= UpCase(C1); C2:= UpCase(C2);

{ Если имена не совпадают и оба достоверны, значит ввод правильный,

в таком случае выходим из цикла, а иначе повторяем ввод }

if (C1<>C2) and (C1 in Names) and (C2 in Names)

then Break

else Writeln('Ошибка! Повторите ввод имен стран');

until False;

{ Подготовка к присоединению стран }

EmpireB:= States[C1]; { Империя начинает расширяться от страны C1 }

EmpireN:= [C1];

{ Здесь накапливаются имена присоединенных стран }

Counter:= 0;

{ Счетчик "завоевательных кампаний" }

{ Цикл, пока не будет присоединена страна C2 }

repeat

 

{ Подготовка к "завоевательной кампании" }

C:='A';

{ Первый индекс в массиве множеств границ }

Temp:= EmpireB; { Запоминаем предыдущие границы империи }

{ Цикл очередной "завоевательной кампании" по всем странам массива }

while C in Names do begin

{ Если страна имеет общую границу, присоединяем её к империи } if (Temp * States[C]) <> [] then begin

EmpireB:=

EmpireB

+

States[C];

{

Добавляем

границы }

EmpireN:=

EmpireN

+

[C];

{

Добавляем

имя страны }

end;

C:= Succ(C); { Следующий индекс в массиве множеств границ }

end; { очередная кампания завершена }

Inc(Counter);

{ Наращиваем счетчик "завоевательных кампаний" }

until C2

in EmpireN;

{ Пока не будет присоединена страна C2 }

{ Печать

результата }

 

Writeln('Пересечений границ: ', Counter); Readln; end.

381

Соседние файлы в папке delphi