Главная · Поиск книг · Поступления книг · Top 40 · Форумы · Ссылки · Читатели

Настройка текста
Перенос строк


    Прохождения игр    
Aliens Vs Predator |#5| Unexpected meeting
Aliens Vs Predator |#4| Boss fight with the Queen
Aliens Vs Predator |#3| Escaping from the captivity of the xenomorph
Aliens Vs Predator |#2| RO part 2 in HELL

Другие игры...


liveinternet.ru: показано число просмотров за 24 часа, посетителей за 24 часа и за сегодня
Rambler's Top100
Образование - Различные авторы Весь текст 991.22 Kb

Программирование в теоремах и задачах

Предыдущая страница Следующая страница
1 2 3 4 5 6 7 8  9 10 11 12 13 14 15 ... 85
  | | | | i := i+ 1;
  | | | end;
  | | | danger := b;
  | | end;
  | end;
  |
  | function is_up: boolean {есть_сверху}
  | begin
  | | is_up := (k < n) and not danger;
  | end;
  |
  | function is_right: boolean {есть_справа}
  | begin
  | | is_right := (k > 0) and (c[k] < n);
  | end;
  | {возможна ошибка: при k=0 не определено c[k]}
  |
  | function is_down: boolean {есть_снизу}
  | begin
  | | is_up := (k > 0);
  | end;
  |
  | procedure up; {вверх_налево}
  | begin {k < n}
  | | k := k + 1;
  | | c [k] := 1;
  | end;
  |
  | procedure right; {вправо}
  | begin {k > 0,  c[k] < n}
  | | c [k] := c [k] + 1;
  | end;
  |
  | procedure down; {вниз}
  | begin {k > 0}
  | | k := k - 1;
  | end;
  |
  | procedure work; {обработать}
  | | var i: integer;
  | begin
  | | if (k = n) and not danger then begin
  | | | for i := 1 to n do begin
  | | | | write ('<', i, ',' , c[i], '> ');
  | | | end;
  | | | writeln;
  | | end;
  | end;
  |
  | procedure UW; {вверх_до_упора_и_обработать}
  | begin
  | | while is_up do begin
  | | | up;
  | | end
  | | work;
  | end;
  |
  begin
  | begin_work;
  | UW;
  | while is_down do begin
  | | if is_right then begin
  | | | right;
  | | | UW;
  | | end else begin
  | | | down;
  | | end;
  | end;
  end.

     3.1.7. Приведенная программа тратит довольно много  времени
на  выполнение  проверки  есть_сверху  (проверка,  находится  ли
верхний ферзь под боем, требует числа действий порядка n). Изме-
нить реализацию операций с деревом позиций так,  чтобы  все  три
проверки есть_сверху/справа/снизу и соответствующие команды тре-
бовали  бы  количества действий, ограниченного не зависящей от n
константой.

     Решение. Для каждой вертикали, каждой восходящей  и  каждой
нисходящей диагонали будем хранить булевское значение - сведения
о том, находится ли на этой линии ферзь (верхний ферзь не учиты-
вается).  (Заметим, что в силу допустимости позиции на каждой из
линий может быть не более одного ферзя.).

     3.2.  Обход дерева в других задачах.

     3.2.1. Использовать метод обхода дерева для решения  следу-
ющей   задачи:   дан  массив  из  n  целых  положительных  чисел
a[1]..a[n] и число s; требуется узнать, может ли  число  s  быть
представлено  как  сумма  некоторых  из чисел массива a. (Каждое
число можно использовать не более чем по одному разу.)

     Решение. Будем задавать k-позицию последовательностью из  k
булевских  значений,  определяющих,  входят  ли  в  сумму  числа
a[1]..a[k] или не входят. Позиция допустима, если  ее  сумма  не
превосходит s.

     Замечание. По сравнению с полным перебором всех (2 в степе-
ни  n) подмножеств тут есть некоторый выигрыш. Можно также пред-
варительно отсортировать массив a в убывающем порядке,  а  также
считать  недопустимыми  те  позиции, в которых сумма отброшенных
членов больше, чем разность суммы всех  членов  и  s.  Последний
приём  называют  "методом  ветвей  и границ". Но принципиального
улучшения по сравнению с полным перебором тут не получается (эта
задача, как говорят, NP-полна,  см.  подробности  в  книге  Ахо,
Хопкрофта и Ульмана "Построение и анализ вычислительных алгорит-
мов").  Традиционное  название  этой задачи - "задача о рюкзаке"
(рюкзак общей грузоподъемностью s нужно упаковать  под  завязку,
располагая  предметами  веса  a[1]..a[n]).  См.  также в главе 7
(раздел о динамическом программировании)  алгоритм  её  решения,
полиномиальный по n+s.

     3.2.2.  Перечислить все последовательности из n нулей, еди-
ниц и двоек, в которых никакая группа цифр  не  повторяется  два
раза подряд (нет куска вида XX).

     3.2.3.  Аналогичная  задача для последовательностей нулей и
единиц, в которых никакая группа цифр не  повторяется  три  раза
подряд (нет куска вида XXX).

     К этой же категории относятся задачи типа "можно ли сложить
данную фигуру из пентамино" и им подобные. В  них  важно  умелое
сокращение  перебора (вовремя распознать, что имеющееся располо-
жение фигурок уже противоречит требованиям, и по этой ветви  по-
иск не продолжать).
        Глава 4. Сортировка.

     4.1. Квадратичные алгоритмы.

     4.1.1. Пусть a[1],  ...,  a[n]  -  целые  числа.  Требуется
построить  массив  b[1],  ..., b[n], содержащий те же числа, для
которых b[1] <= ... <= b[n].
     Замечание. Среди чисел a[1]...a[n] могут быть равные.  Тре-
буется,  чтобы  каждое целое число входило в b[1]...b[n] столько
же раз, сколько и в a[1]...a[n].

     Решение. Удобно считать, что числа a[1]..a[n] и  b[1]..b[n]
представляют собой начальное и конечное значения массива x. Тре-
бование  "a  и b содержат одни и те же числа" будет заведомо вы-
полнено, если в процессе работы  мы  ограничимся  перестановками
элементов x.
  ...
  k := 0;
  {k наименьших элементов массива x установлены на свои места}
  while k <> n do begin
  | s := k + 1; t := k + 1;
  | {x[s] - наименьший среди x[k+1]...x[t] }
  | while t<>n do begin
  | | t := t + 1;
  | | if x[t] < x[s] then begin
  | | | s := t;
  | | end;
  | end;
  | {x[s] - наименьший среди x[k+1]..x[n] }
  | ... переставить x[s] и x[k+1];
  | k := k + 1;
  end;

     4.1.2.  Дать другое решение задачи сортировки, использующее
инвариант {первые k элементов упорядочены: x[1] <= ... <= x[k]}

     Решение.

  k:=1
  {первые k элементов упорядочены}
  while k <> n do begin
  | {k+1-ый элемент продвигается к началу, пока не займет
  |   надлежащего места }
  | t := k+1;
  | {x[1] <= ... <= x[t-1] и x[t-1], x[t] <= ... <= x[k+1] }
  | while (t > 1) and (x[t] < x[t-1]) do begin
  | | ... поменять x[t-1] и x[t];
  | | t := t - 1;
  | end;
  end;

     Замечание. Дефект программы: при ложном выражении (t  >  1)
проверка x[t] < x[t-1] требует несуществующего значения x[0].
     Оба  предложенных решения требуют числа действий, пропорци-
онального n*n. Существуют более эффективные алгоритмы.

     4.2. Алгоритмы порядка n log n.

     4.2.1. Предложить алгоритм сортировки, число действий кото-
рого  было  бы  порядка  n  log  n,  то  есть не превосходило бы
C*n*log(n) для некоторого C и для всех n.

     Мы предложим два решения.

     Решение 1. (сортировка слиянием).
     Пусть  k  -  положительное  целое  число.  Разобьем  массив
x[1]..x[n]  на  отрезки  длины  k.  (Первый  - x[1]..x[k], затем
x[k+1]..x[2k] и т.д.) Последний отрезок будет неполным,  если  n
не  делится на k. Назовем массив k-упорядоченным, если каждый из
этих отрезков упорядочен. Любой массив 1-упорядочен. Если массив
k-упорядочен и n<=k, то он упорядочен.
     Мы  опишем,  как  преобразовать  k-упорядоченный  массив  в
2k-упорядоченный (из тех же элементов). С помощью этого преобра-
зования алгоритм записывается так:

  k:=1;
  {массив x является k-упорядоченным}
  while k < n do begin
  | .. преобразовать k-упорядоченный массив в 2k-упорядоченный;
  | k := 2 * k;
  end;

     Требуемое  преобразование  состоит в том,что мы многократно
"сливаем" два упорядоченных отрезка длины не  больше  k  в  один
упорядоченный  отрезок. Пусть процедура слияние (p,q,r: integer)
при p <=q <= r сливает отрезки  x[p+1]..x[q]  и  x[q+1]..x[r]  в
упорядоченный  отрезок x[p+1]..x[r] (не затрагивая других частей
массива x).
                  p               q               r
            -------|---------------|---------------|-------
                   | упорядоченный | упорядоченный |
            -------|---------------|---------------|-------
                                  |
                                  |
                                  V
            -------|-------------------------------|-------
                   |     упорядоченный             |
            -------|-------------------------------|-------

Тогда преобразование k-упорядоченного массива в 2k-упорядоченный
осуществляется так:

  t:=0;
  {t кратно 2k или t = n, x[1]..x[t] является
   2k-упорядоченным; остаток массива x не изменился}
  while t + k < n do begin
  | p := t;
  | q := t+k;
  | ...r := min (t+2*k, n); {в паскале нет функции min }
  | слияние (p,q,r);
  | t := r;
  end;

Слияние требует вспомогательного массива для записи  результатов
слияния  -  обозначим его b. Через p0 и q0 обозначим номера пос-
ледних элементов участков, подвергшихся слиянию, s0 -  последний
записанный  в  массив b элемент. На каждом шаге слияния произво-
дится одно из двух действий:

        b[s0+1]:=x[p0+1];
        p0:=p0+1;
        s0:=s0+1;
или
        b[s0+1]:=x[q0+1];
        q0:=q0+1;
        s0:=s0+1;

Первое действие (взятие элемента из первого отрезка) может  про-
изводиться при двух условиях:
    (1) первый отрезок не кончился (p0 < q);
    (2) второй отрезок кончился (q0 = r)  или  не  кончился,  но
элемент в нем не меньше [(q0 < r) и (x[p0+1] <= x[q0+1])].
     Аналогично для второго действия. Итак, получаем

  p0 := p; q0 := q; s0 := p;
  while (p0 <> q) or (q0 <> r) do begin
  | if (p0 < q) and ((q0 = r) or ((q0 < r) and
  | |                (x[p0+1] <= x[q0+1]))) then begin
  | | b [s0+1] := x [p0+1];
  | | p0 := p0+1;
  | | s0 := s0+1;
  | end else begin
  | | {(q0 < r) and ((p0 = q) or ((p0= x[q0+1])))}
  | | b [s0+1] := x [q0+1];
  | | q0 := q0 + 1;
  | | s0 := s0 + 1;
  | end;
  end;

(Если оба отрезка не кончены и первые невыбранные элементы в них
равны, то допустимы оба действия; в программе выбрано первое.)
     Программа  имеет  привычный дефект: обращение к несуществу-
ющим элементам массива при вычислении булевских выражений.

     Решение 2 (сортировка деревом).
     Нарисуем "полное двоичное дерево"  -  картинку,  в  которой
снизу один кружок, из него выходят стрелки в два других, из каж-
дого - в два других и так далее:

               .............
                 o  o o  o
                  \/   \/
                   o   o
                    \ /
                     o


     Будем  говорить, что стрелки ведут "от отцов к сыновьям": у
каждого кружка два сына и один отец (если  кружок  не  верхний).
Предположим  для  простоты, что количество подлежащих сортировке
чисел есть степень двойки, и они могут заполнить один  из  рядов
целиком. Запишем их туда. Затем заполним часть дерева под ним по
правилу:
   число в кружке = минимум из чисел в кружках-сыновьях
Тем  самым  в  корне дерева (нижнем кружке) будет записано мини-
мальное число во всем массиве.
     Изымем из сортируемого  массива  минимальный  элемент.  Для
этого  его  надо вначале найти. Это можно сделать, идя от корня:
от отца переходим к тому сыну, где записано то же  число.  Изъяв
минимальный  элемент,  заменим  его  символом  "бесконечность" и
скорректируем более низкие ярусы (для этого  надо  снова  пройти
путь к корню). При этом считаем, что минимум из n и бесконечнос-
ти  равен  n. Тогда в корне появится второй по величине элемент,
мы изымаем его, заменяя бесконечностью и корректируя дерево. Так
постепенно мы изымем все элементы в порядке возрастания, пока  в
корне не останется бесконечность.
     При записи этого алгоритма полезно нумеровать кружочки чис-
лами 1, 2, ...: сыновьями кружка номер n являются кружки  2*n  и
2*n+1. Подробное изложение этого алгоритма мы опустим, поскольку
мы  изложим  более  эффективный  вариант,  не требующий дополни-
тельной памяти, кроме конечного числа переменных (в дополнении к
сортируемому массиву).
     Мы будем записывать сортируемые числа во всех вершинах  де-
рева,  а не только на верхнем уровне. Пусть x[1]..x[n] - массив,
подлежащий сортировке. Вершинами дерева будут числа от 1 до n; о
числе x[i] мы будем говорить как о числе, стоящем в вершине i. В
процессе сортировки количество вершин дерева будет  сокращаться.
Число вершин текущего дерева будем хранить в переменной k. Таким
образом,  в  процессе работы алгоритма массив x[1]..x[n] делится
на две части: в x[1]..x[k] хранятся числа на дереве, а в  x[k+1]
Предыдущая страница Следующая страница
1 2 3 4 5 6 7 8  9 10 11 12 13 14 15 ... 85
Ваша оценка:
Комментарий:
  Подпись:
(Чтобы комментарии всегда подписывались Вашим именем, можете зарегистрироваться в Клубе читателей)
  Сайт:
 
Комментарии (1)

Реклама