Добавил:
Upload Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:
Алгоритмы и структуры данных.doc
Скачиваний:
0
Добавлен:
01.03.2025
Размер:
1.14 Mб
Скачать

Var k: integer; {Количество нулей в кортеже}

M: integer; {Количество нулей в кортеже}

I: integer;

GB: Arr;

Finish: boolean;

procedure SubSet (var LB: Arr; NM: integer; var Finish: boolean);

Var I, j, p: integer;

Z: integer; {Количество хвостовых нулей}

P: integer; {Количество хвостовых единиц, предшествующих хвостовым нулям }

begin

J := N; {Движение от конца массива LB к его началу}

while LB[J] <> 1 do J := J - 1; {Пропуск хвостовых нулей}

Z := N - J + 1; {Подсчет количества хвостовых нулей z плюс 1, т.е. Z = z+1}

P := 0;

J := J - 1;{Корректировка возможного количества 1}

while (J <> 0) and (LB[J] <> 0) do {Подсчет количества хвостовых единиц p минус 1 (т.е. P = p-1), предшест­вующих z хвостовым нулям}

begin

P := P + 1;

J := J - 1;

end;

if J = 0 then{Получена конечная перестановка 1 1 1 0 0}

begin

Finish := true;

Exit;

end;

{Формирование новой перестановки}

LB[J] := 1; {Размещение новой 1}

for I := 1 to Z do {Размещение группы z+1 нулей}

begin

J := J + 1;

LB[J] := 0;

end;

if P = 0 then Exit; {Если была всего одна 1}

for I := 1 to P do {Размещение группы p-1 единиц}

begin

J := J + 1;

LB [J] := 1;

end;

Exit;

end; {SubSet}

begin

Clrscr;

Write ('Введите количество членов подмножества (меньше ', N, ' ) -> ');

Readln (K);

M := N - K; {Действительное количество нулей в кортеже}

{Исходное состояние массива GB - исходный кортеж}

for I := 1 to M do GB[I] := 0;

for I := M + 1 to N do GB[I] := 1;

Write (' ');

for I := 1 to N do Write (' ', GB[I]);

Writeln;

{Генерация остальных кортежей}

Finish := false;

while true {not Finish} do

begin

SubSet (GB, M, Finish);

If Finish then Break {Exit};

Write (' ');

for I := 1 to N do Write (' ', GB[I]);

Writeln;

end;

end.

6Генерация разбиений

6.1Разбиение целых чисел

Задача разбиения целых чисел сводится к определению количества способов, с помощью которых можно записать заданное число N в виде суммы

N = a1 + a2 + ai, +… + ak,

где i, k, a1, a2, …, ai,…, ak > 0 и записи, отличающиеся только порядком слагаемых, рассматриваются как одна запись.

Пусть N =4. Тогда получим следующие разбиения

4

3 1

2 2

2 1 1

1 1 1 1

Ниже приводится текст программы DivideN, которая генерирует все перестановки числа N. Для представления разбиения числа программа использует массивы SS и RR. В массиве SS содержится набор попарно различных слагаемых, образующих число N. Эти слагаемые (элементы массива SS) упорядочены по убыванию. В массиве RR содержится количество вхождений каждого соответствующего слагаемого в число N. Например, для разбиения:

  • 2 2 получим SS[1] = 2, а RR[1] =2;

  • 2 1 1 получим SS[1] =2 и SS[2]= 1, а RR[1] = 1 и RR[2] = 2.

Каждое новое разбиение строится на основе предыдущего разбиения. В текущем разбиении отыскивается элемент SS[d], такой что

d = max{i: SS[i] > 1}

т.е. самый правый элемент в массиве SS больший 1. Это может быть последний или предпоследний элемент разбиения.

Случай, когда SS[d] является последним элементом разбиения. Тогда проверяется RR[d] - количество вхождений значения этого элемента в разбиение. Если значение SS[d] входит в разбиение несколько раз, т.е. RR[d] > 1, то количество его вхождений уменьшается на 1

RR[d] := RR[d] – 1

а выделенное вхождение, в зависимости от его значения, представляется с помощью комбинации

SS[d+1] и RR[d+1]

или комбинации

SS[d+1], SS[d+2] и RR[d+1], RR[d+2].

Примечание. Способ получения значений для приведенных комбинаций легко понять при рассмотрении текста приведенной ниже программы.

Пусть N = 10 и задано разбиение, где d = 2:

1

2

SS

4

2

RR

1

3

Тогда новое разбиение будет иметь вид:

1

2

3

SS

4

2

1

RR

1

2

2

т.е. добавилась комбинация SS[3] и RR[3]

Пусть теперь задано разбиение, где d = 2:

1

2

SS

4

3

RR

1

2

Тогда новое разбиение будет иметь вид:

1

2

3

4

SS

4

3

2

1

RR

1

1

1

1

т.е. добавилась комбинация SS[3], RR[3] и SS[4], RR[4]

Если значение SS[d] входит в разбиение один раз, т.е. RR[d] = 1, то SS[d] уменьшается на 1

SS[d] := SS[d] – 1

а RR[d] устанавливается в 1

RR[d] := 1

Здесь возникает вопрос, куда отнести оставшуюся после вычитания 1. Если до уменьшения SS[d] имело место SS[d] > 2, то формируется

SS[d+1] := 1 и RR[d+1] := 1

В противном случае

SS[d] := 1 и RR[d] := 2.

Пусть задано разбиение, где d = 2:

1

2

SS

7

3

RR

1

1

Тогда новое разбиение будет иметь вид

1

2

3

SS

7

2

1

RR

1

1

1

т.е. добавилась комбинация SS[3] и RR[3].

Пусть теперь задано разбиение, где d = 2:

1

2

SS

8

2

RR

1

1

Тогда новое разбиение будет иметь вид:

1

2

SS

8

1

RR

1

2

т.е. выполнена корректировка комбинации SS[3] и RR[3].

Случай, когда SS[d] является предпоследним элементом разбиения. Тогда SS[d+1] = 1, а RR[d+1] равно количеству вхождений 1 в разбиение. В этом случае запоминается содержимое т.е. сохраняется количество 1 в разбиении

Sum := RR[d+1]

а сама комбинация SS[d+1] и RR[d+1] удаляются из разбиения. Фактически задача сводится к рассмотренному выше случаю, когда SS[d] является последним элементом разбиения, но со следующими изменениями - из разбиения изымается один из элементов SS[d], а количество его вхождений в разбиение RR[d] уменьшается на 1

RR[d] := RR[d] – 1

Изъятый элемент суммируется с сохраненным количеством 1 в разбиении. Далее возможны два варианта продолжения в зависимости от полученного значения RR[d]:

  • если RR[d] = 0, то модифицируется комбинация

SS[d] и RR[d]

и, в зависимости от значения полученной суммы, может добавиться комбинация

SS[d+1] и RR[d+1]

  • если RR[d] <> 0, то модифицируется комбинация

SS[d] и RR[d]

и, в зависимости от значения полученной суммы, может добавиться комбинация

SS[d+1] и RR[d+1]

или комбинации

SS[d+1], SS[d+2] и RR[d+1], RR[d+2].

Пусть задано разбиение, где d = 2:

1

2

3

SS

6

2

1

RR

1

1

2

Тогда новое разбиение будет иметь вид:

1

2

SS

6

1

RR

1

4

и модифицируется комбинация SS[2] и RR[2].

Пусть задано разбиение, где d = 2:

1

2

3

SS

5

4

1

RR

1

1

1

Тогда новое разбиение будет иметь вид:

1

2

3

SS

5

3

2

RR

1

1

1

и модифицируется комбинация SS[2] и RR[2], а также заново вводится комбинация SS[3] и RR[3].

Пусть задано разбиение, где d = 2:

1

2

3

SS

5

2

1

RR

1

2

1

Тогда новое разбиение будет иметь вид:

1

2

3

SS

5

2

1

RR

1

1

3

и заново вводится комбинация SS[3] и RR[3].

В программе предусмотрены два способа вывода результата. Первый способ задается парой вызовов Output1(S) и Output1(R). В результате выводится содержимое массивов – SS и RR соответственно. Второй способ задается вызовом Output2(S, R). В результате выводится разбиение числа в виде:

N = a1 + a2 + ai, +… + ak

Текст программы приводится ниже.

program DivideN;

{Генерирует все разбиения числа N}

uses Crt;

const N = 8;

type TArr = array [1..N] of integer;