| Россия, Пошатово |
Переменные, выражения, присваивания
1.2.27.
(Двоичный поиск) Дана последовательность
целых чисел
и число a.
Выяснить, содержится ли a в этой последовательности, то
есть существует ли i из 1..n, для которого
. (Количество действий порядка
.)
Решение.
(Предполагаем, что
.)
l := 1; r := n+1;
{r > l, если a есть вообще, то есть и среди x[l]..x[r-1]}
while r - l <> 1 do begin
| m := l + (r-l) div 2 ;
| {l < m < r }
| if x[m] <= a then begin
| | l := m;
| end else begin {x[m] > a}
| | r := m;
| end;
end;(Обратите внимание, что и в случае
инвариант не нарушается.)
Каждый раз
уменьшается примерно вдвое, откуда
и вытекает требуемая оценка числа действий.
Замечание.

В этой задаче существенно, что массив упорядочен - поиск в неупорядоченном массиве требует времени, пропорционального длине массива. (Чтобы убедиться, что какого-то числа нет в массиве, надо просмотреть все его элементы.)
1.2.28. (Из книги Д. Гриса) Имеется массив x: array[1..n] of array[1..m] of integer, упорядоченный по строкам и по столбцам:
![x[i][j] \le x[i][j+1],\\
x[i][j] \le x[i+1][j],](/sites/default/files/tex_cache/023cd31721be4bb8122415e72129aa77.png)
Решение. Представляя себе массив x как матрицу
(прямоугольник, заполненный числами), мы выберем
прямоугольник, в котором только и может содержаться a,
и будем его сужать. Прямоугольник этот будет содержать x[i][j] при
и 
и
).l:=n; k:=1;
{l>=0, k<=m+1, если a есть, то в описанном прямоугольнике}
while (l > 0) and (k < m+1) and (x[l][k] <> a) do begin
| if x[l][k] < a then begin
| | k := k + 1; {левый столбец не содержит a, удаляем его}
| end else begin {x[l][k] > a}
| | l := l - 1; {нижняя строка не содержит a, удаляем ее}
| end;
end;
{x[l][k] = a или прямоугольник пуст }
answer:= (l > 0) and (k < m+1) ;Замечание. Здесь та же ошибка: x[l][k] может оказаться неопределенным. (Ее исправление предоставляется читателю.)
1.2.29.
(Московская олимпиада по программированию) Дан неубывающий
массив положительных целых чисел
. Найти наименьшее
целое положительное число, не представимое в виде суммы
нескольких элементов этого массива (каждый элемент массива
может быть использован не более одного раза). Число
действий порядка n.
Решение. Пусть известно, что числа, представимые в виде
суммы элементов
, заполняют
отрезок от 1 до некоторого N. Если
, то
и будет минимальным числом, не
представимым в виде суммы элементов массива
. Если же
,
то числа, представимые в виде суммы элементов
, заполняют отрезок от 1 до N+a[k+1].
k := 0; N := 0;
{инвариант: числа, представимые в виде суммы элементов
массива a[1]..a[k], заполняют отрезок 1..N}
while (k <> n) and (a[k+1] <= N+1) do begin
| N := N + a[k+1];
| k := k + 1;
end;
{(k = n) или (a[k+1] > N+1); в обоих случаях ответ N+1}
writeln (N+1);(Снова тот же дефект: в условии цикла при ложном первом условии второе не определено.)
1.2.30.
(Для знакомых с основами алгебры) В целочисленном массиве
хранится перестановка чисел
(каждое из чисел встречается по одному
разу).
(а) Определить четность перестановки. (И в (а), и в (б)
количество действий порядка
.)
(б) Не используя других массивов, заменить перестановку на
обратную (если до работы программы
, то
после должно быть
).
Указание. (а) Четность перестановки определяется количеством циклов. Чтобы отличать уже пройденные циклы, у их элементов можно, например, менять знак. (б) Обращение производим по циклам.
1.2.31. Дан массив a[1..n] и число b. Переставить числа в массиве таким образом, чтобы слева от некоторой границы стояли числа, меньшие или равные b, а справа от границы - большие или равные b. Число действий порядка n.
Решение.
l:=0; r:=n;
{инвариант: a[1]..a[l]<=b; a[r+1]..a[n]>=b}
while l <> r do begin
| if a[l+1] <= b then begin
| | l:=l+1;
| end else if a[r] >=b then begin
| | r:=r-1;
| end else begin {a[l+1]>b; a[r]<b}
| | ..поменять a[l+1] и a[r]
| | l:=l+1; r:=r-1;
| end;
end;1.2.32. Та же задача, но требуется, чтобы сначала шли элементы, меньшие b, затем равные b, а лишь затем большие b.
Решение. Теперь потребуются три границы: до первой будут идти элементы, меньшие b, от первой до второй - равные b, затем неизвестно какие до третьей, а после третьей - большие b. (Более симметричное решение использовало бы четыре границы, но вряд ли игра стоит свеч.) В качестве очередного рассматриваемого элемента берем элемент справа от средней границы.
l:=0; m:=0; r:=n;
{инвариант: a[1..l]<b; a[l+1..m]=b; a[r+1]..a[n]>b}
while m <> r do begin
| if a[m+1]=b then begin
| | m:=m+1;
| end else if a[m+1] > b then begin
| | ..обменять a[m+1] и a[r]
| | r:=r-1;
| end else begin {a[m+1] < b}
| | ..обменять a[m+1] и a[l+1]
| | l:=l+1; m:=m+1;
| end;
end;1.2.33. (Вариант предыдущей задачи, названный в книге Дейкстры задачей о голландском флаге.) В массиве длины n стоят числа 0, 1 и 2. Переставить их в порядке возрастания, если единственной разрешенной операцией (помимо чтения) над массивом является перестановка двух элементов. Число действий порядка n.
1.2.34.
Дан массив a[1..n] и число
. Для
каждого участка из m стоящих рядом членов (таких
участков, очевидно,
) вычислить его
сумму. Общее число действий должно быть порядка n.
Решение. Переходя от участка к соседнему, мы добавляем один член, а другой вычитаем.
1.2.35.
Дана квадратная таблица a[1..n][1..n] и число
. Для каждого квадрата
в этой таблице вычислить сумму стоящих в нем чисел. Общее
число действий порядка
.
Решение. Сначала для каждого горизонтального
прямоугольника размером
вычисляем сумму
стоящих в нем чисел. (При сдвиге такого прямоугольника по
горизонтали на 1 нужно добавить одно число и одно
вычесть.) Затем, используя эти суммы, вычисляем суммы
в квадратах. (При сдвиге квадрата по вертикали добавляется
полоска, а другая полоска убавляется.)
1.2.36.
В массиве
встречаются по одному
разу все целые числа от 0 до n, кроме одного. Найти
пропущенное число за время порядка n и с конечной
дополнительной памятью.
Указание. Сложить все числа в массиве.
