Hosted by uCoz

Задача № 45. Проверить, является ли последовательность строго монотонной

Формулировка. Дана последовательность натуральных чисел, ограниченная вводом нуля. Проверить, является ли эта последовательность строго монотонной.

Решение. Эта задача – упрощенный вариант задачи 32. Вообще, эти две задачи логично было бы в данном сборнике поменять местами, однако она оказалась на этой позиции из-за тематического распределения задач.

Единственное отличие от задачи 32 состоит в том, что в нашем случае члены последовательности вводятся с клавиатуры с помощью оператора read() – их не нужно добывать как цифры некоторого числа.

Воспользуемся формулой (II) из задачи 32:

p = delta1 * deltai

Напомним, что если произведение p отрицательно или равно нулю, то последовательность не строго монотонна, так как нашлись два delta разных знаков. Мы будем двигаться по последовательности в порядке ввода, слева направо, исследуя при этом знаки всех произведений p. Так как delta1 присутствует в формуле в качестве константы, его значение необходимо вычислить заранее:

read(a, b);

delta := a – b;

В цикле мы будем на каждом шаге считывать один очередной член, поэтому необходимо «сдвигать последовательность» и считывать его в освободившуюся переменную. Так как в переменной a хранится левый член каждой пары, а в переменной b – правый член, то очередное число мы будем считывать в переменную b. Так как ограничитель последовательности – ноль, то и цикл будет продолжаться до ввода в b нуля:

while b <> 0 do begin

  if delta * (a - b) <= 0 then break;

  a := b;

  read(b)

end;

Здесь в первом операторе тела цикла происходит проверка произведения каждых двух delta по уже упомянутой формуле (II) из задачи 32. Далее идет «сдвиг» правого члена текущей пары и ввод нового элемента вместо него. Проще говоря, если, например, у нас в a находился 1-й член последовательности, а в b – 2-й, то данным способом мы переносим 2-й член в переменную a и считываем 3-й член в переменную b, после чего можно проводить следующее сравнение.

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

Каким же будет развитие событий после выхода из цикла?

1)   Если выход был осуществлен через break, то есть по условию нарушения строгой монотонности, то можно вывести на экран значение выражения delta * (a b) > 0, что даст ответ false;

2)   Если цикл завершился по вводу нуля, то последовательность строго монотонна, и нужно, соответственно, выводить ответ true. Однако здесь мы сталкиваемся с проблемой из задачи 45, связанной с тем, что вводимый ноль не обрабатывается в основном цикле, так как не входит в последовательность, однако он вводится в обрабатываемую переменную b, чтобы можно было выйти из цикла. Однако из-за этого с помощью оператора writeln(delta * (ab) > 0) мы можем получить неправильный ответ, так как в последовательность обрабатывается с вводимым нулем включительно.

Например, последовательность 1 2 3 0 строго монотонна, хотя программа выдаст ответ false, потому что по выходе из цикла delta будет содержать число –1, a – число 3, b – число 0, и выражение –1 * (3 – 0) > 0 – неверно.

На этот раз мы справимся с проблемой по-другому. Легко понять, что если после выхода из цикла b = 0, то последовательность строго монотонна, так как проверка прошла по всем delta вплоть до ввода ограничителя. Если же после выхода b отлично от 0, то был совершен выход по break в теле цикла и последовательность не является строго монотонной. Поэтому логично оформить вывод ответа так:

writeln(b = 0);

Кстати, в такой форме можно осуществить вывод и в задачах 32, 43 и 44, с некоторым, однако, изменением инициализации входных значений переменных.

Напоследок необходимо позаботиться о правильной обработке вырожденных случаев. Будем считать последовательность из единственного нуля не строго монотонной, в отличие от последовательности из одного члена. Она, кстати, итак уже будет обрабатываться корректно: в a вводится некоторое число, а в b вводится 0. При этом не осуществляется вход в основной цикл, и программа переходит к выводу выражения b = 0, которое верно.

Сделаем корректной обработку пустой последовательности. Во-первых, необходимо дать возможность ввода таковой, так как в нашем наброске требуется ввод минимум двух чисел. Для этого мы будем считывать сначала a, и если оно отлично от нуля, то считывать b:

read(a);

if a <> 0 then read(b) else b := 0;

При этом если a = 0, то мы обязательно присваиваем значение 0 переменной b, чтобы она была определена, и гарантированно не было входа в цикл. Однако в этом случае вывод выражения b = 0 повлечет вывод true. Чтобы избежать этого, нужна еще одна проверка: если a = 0, то присвоить b натуральное число, отличное от 0 (например, 1), что повлечет вывод false:

if a = 0 then b := 1;

Код:

  1. program MonotonicSequence;
  2. var
  3. a, b: word;
  4. delta: integer;
  5. begin
  6. read(a);
  7. if a <> 0 then read(b) else b := 0;
  8. delta := a - b;
  9. while b <> 0 do begin
  10. if delta * (a - b) <= 0 then break;
  11. a := b;
  12. read(b)
  13. end;
  14. if a = 0 then b := 1;
  15. writeln(b = 0)
  16. end.

Стоит отметить, что на первом шаге в основном цикле значение a и b еще не изменено, и грубо говоря, delta в строке 12 умножается само на себя. Это нужно для того, чтобы обеспечить правильную обработку последовательности из двух одинаковых чисел, которая не является строго монотонной. Например, если на вход подается последовательность k k 0, k – некоторое натуральное число, то delta будет равно 0 (строка 10), и при входе в цикл будет осуществлен выход по break (строка 12), что повлечет вывод false, так как b отлично от 0.

Если бы мы в цикле сначала осуществили сдвиг последовательности и ввод третьего члена (то есть, переместили бы строки 13-14 на одну позицию вверх, а строку 12 поместили бы после них), то по выходе из цикла через break b уже было бы равно 0, что повлекло бы вывод true, а это неверно.