Разбор задач части С
Задача С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 работ. Членам комиссии было известно, что решения задач были выложены в интернете. Доступными были рекомендованные решения, то есть те самые решения, которые представлялись комиссиям, проверяющим работы. Так что вина за утечку информации лежит не на школьниках, а на людях, отвечающих за честное проведение экзамена.
Представьте себе положение членов комиссии, которые видят, что представленное школьником решение буква в букву повторяет рекомендованное решение. У нас нет никаких сомнений, что решение списано, поскольку оно буквально повторяет рекомендованное решение, которое кстати говоря является далеко не идеальным, поскольку включает лишние проверки и довольно запутанным образом проверяет условие, накладываемое на максимальный элемент. Мы знаем, что представлена работа, не заслуживающая какой-либо положительной оценки, которую следует оценить на "неудовлетворительно".
У нас нет права занижать оценку за списывание. Мы видим правильное решение, должны по правилам ставить максимальный балл, понимая, что оценка не заслужена, что лучшие оценки ЕГЭ получают не лучшие ученики, а обманщики.
