Разбор задач части С
Задача С4 (Алгоритмически сложная задача)
В разделе С это вторая задача на программирование. Если задача С2 требует от школьника знания классического алгоритма, то для "красивого" решения задачи С4 требуется придумать нестандартный алгоритм. Приведу вариант задачи, данный на экзамене.
<<
По каналу связи передается последовательность положительных целых чисел, все числа не превышают 1000. Количество чисел известно, но может быть очень велико. Затем передается контрольное значение последовательности – наибольшее число R, удовлетворяющее следующим условиям:
- R – произведение двух различных переданных элементов последовательности ("различные" означает, что не рассматриваются квадраты переданных чисел, произведения различных элементов последовательности, равных по величине, допускаются).
- R делится на 35.
Если такого числа R нет, то контрольное значение полагается равным 0.
В результате помех при передаче как сами числа, так и контрольное значение могут быть искажены. Напишите эффективную, в том числе по используемой памяти, программу, которая будет проверять правильность контрольного значения. Программа должна напечатать отчет по следующей форме:
Вычисленное контрольное значение: - …
Перед текстом программы кратко опишите алгоритм.
На вход программе в первой строке подается количество чисел N. В каждой из последующих N строк записано одно натуральное число, не превышающее 1000. В последней строке записано контрольное значение.
>>
Прежде чем перейти к рассмотрению алгоритма, сделаю замечание по поводу формулировки задачи. Она излишне усложнена, возможно такая формулировка годилась бы для олимпиады, но никак не подходит к задаче, предлагаемой на общем экзамене. Вот как следовало бы поставить задачу для школьников, сохранив суть, но убрав излишества:
Вычислить R - максимальное произведение двух элементов массива, при условии, что R делится на 35.
Задача формулируется в одну строчку, суть ее сохраняется, но все запутывающие "завитушки" о контрольных значениях, разных элементах устраняются.
У задачи есть элегантное решение, а есть и неэффективное переборное решение. К сожалению, система оценивания решения не совершенна. Школьник, представивший неэффективное переборное решение получит лишь на балл меньше того школьника, который представит элегантное решение, не требующее запоминания всех чисел и перебора всех возможных значений.
Приступим к разбору решения. Рассмотрим оба решения этой задачи. Первое достаточно очевидное. Нужно прочитать все числа, запомнить их в массиве. Тогда задача сводится к известной задаче нахождения максимума в массиве. Нужно найти максимальное произведение двух элементов, удовлетворяющее дополнительным условиям. Для этого достаточно организовать цикл по i и внутренний цикл по j, вычисляя произведение элементов a[i] * a[j], анализируя его на выполнение дополнительного условия и сравнивая с текущим максимальным значением произведения элементов. Приведу текст соответствующей программы на языке С#:
static void Main() { int N; Console.WriteLine("Введите количество элементов массива"); N = int.Parse(Console.ReadLine( )); int[] a = new int[N]; Console.WriteLine("Введите элементы массива"); for(int i =0; i <N; i++) a[i] = int.Parse(Console.ReadLine()); int R = 0; int cand =0; for(int i = 0; i < N; i++) for(int j = i+1; j < N; j++) { cand = a[i] * a[j]; if (cand % 35 == 0 && cand > R) R = cand; } Console.WriteLine("R = " + R); }
В таком варианте программа достаточно проста и доступна для школьников, умеющих писать простые программы. Что касается "завитушек", введенных в формулировку данной задачи, то первое требование о "разных" элементах выполняется автоматически, поскольку внутренний цикл начинается со значения i +1, так что квадраты чисел просто не строятся. "Завитушка" насчет контроля значений просто означает, что нужно ввести "контрольное" значение R и сравнить его с вычисленным. Я не стал этого делать, дабы не затенять суть этой простой программы.
Я бы рекомендовал школьникам, которым на экзамене встречается подобная задача, вначале написать простой, хотя и не эффективный алгоритм. Преимущество его в том, что он короток, прост, в нем трудно сделать ошибки, а оценивается он почти также как и более сложный эффективный алгоритм. Написав эту программу, можно при наличии времени приступить к написанию программы, реализующей эффективный алгоритм.
Давайте рассмотрим эффективный по памяти и быстродействию алгоритм, который не требует запоминания массива и не требует введения внутреннего цикла. Идея алгоритма основана на том, что на разыскиваемое произведение двух элементов накладывается дополнительное условие – произведение должно делиться на 35. Поскольку число 35 имеет только два простых сомножителя, то большинство элементов в массиве можно игнорировать. После небольших размышлений можно понять, что есть два претендента на ответ:
cand1 = max5 * max7;
Здесь:
- max5 – это максимальный элемент среди тех элементов, которые делятся на 5, но не делятся на 35;
- max7– это максимальный элемент среди тех элементов, которые делятся на 7, но не делятся на 35;
- max35 – это максимальный элемент среди элементов, которые делятся на 35;
- max – это максимальный элемент массива. Учитывая "завитушку" о разных элементах, на max накладывается дополнительное условие, - его индекс не должен совпадать с индексом элемента, представляющего max35
Эти рассуждения позволяют построить эффективную программу, не требующую запоминания чисел в массиве и позволяющую выполнить все основные вычисления за один проход по данным.
Вот как может выглядеть подобная программа, написанная на языке С#:
static void Main(string[] args) { int N; Console.WriteLine("Введите количество элементов массива"); N = int.Parse(Console.ReadLine()); int item; int max, max5, max7, max35; int index_max35; max = max5 = max7 = max35 = 0; index_max35 = 0; Console.WriteLine("Введите поочередно элементы массива"); for (int i = 0; i < N; i++) { item = int.Parse(Console.ReadLine()); if (item % 35 == 0 && item > max35) { max35 = item; index_max35 = i; } else { if (item % 5 == 0 && item > max5) max5 = item; if (item % 7 == 0 && item > max7) max7 = item; } if (i != index_max35 && item > max) max = item; } int R,R_Contr, cand1, cand2; cand1 = max5 * max7; cand2 = max35 * max; if (cand1 > cand2) R = cand1; else R = cand2; Console.WriteLine("Вычислено значение R = " + R); Console.WriteLine("Введите контрольное значение"); R_Contr = int.Parse(Console.ReadLine()); if (R == R_Contr) Console.WriteLine("Контроль пройден"); else Console.WriteLine("Контроль не пройден"); }
В этом варианте учтены все требования к программе. Выполнена проверка того, что произведение не является квадратом числа, также как и проверка совпадения с контрольным значением.
Подводя общие итоги экзамена по информатике, можно отметить, что наиболее легкой была задача С3, с которой справилось большинство школьников, представивших свои работы. Многие школьники представили корректное решение задачи С1. С программированием дело обстоит значительно хуже. Сравнительно простая задача С2 для многих оказалась трудной задачей. Совсем плохая ситуация сложилась с задачей С4, но не потому, что не было представлено ее правильных решений. Но об этой ситуации следует сказать особо.
Печальные итоги проверки экзамена по информатике
Я участвовал в работе комиссии по проверке части С экзамена по информатике. Комиссии было представлено более 500 работ. Членам комиссии было известно, что решения задач были выложены в интернете. Доступными были рекомендованные решения, то есть те самые решения, которые представлялись комиссиям, проверяющим работы. Так что вина за утечку информации лежит не на школьниках, а на людях, отвечающих за честное проведение экзамена.
Представьте себе положение членов комиссии, которые видят, что представленное школьником решение буква в букву повторяет рекомендованное решение. У нас нет никаких сомнений, что решение списано, поскольку оно буквально повторяет рекомендованное решение, которое кстати говоря является далеко не идеальным, поскольку включает лишние проверки и довольно запутанным образом проверяет условие, накладываемое на максимальный элемент. Мы знаем, что представлена работа, не заслуживающая какой-либо положительной оценки, которую следует оценить на "неудовлетворительно".
У нас нет права занижать оценку за списывание. Мы видим правильное решение, должны по правилам ставить максимальный балл, понимая, что оценка не заслужена, что лучшие оценки ЕГЭ получают не лучшие ученики, а обманщики.