Добавил:
Upload Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:

Lab04_2010

.pdf
Скачиваний:
27
Добавлен:
07.06.2015
Размер:
1.13 Mб
Скачать

Лабораторная работа 4. Работа с автоматизированной системой проверки

В данной лабораторной работе описывается взаимодействие с автоматизированной проверяющей системой, расположенной на сайте http://contest.samara.ru. Эта система содержит архив задач как учебного, так и олимпиадного характера. Она принимает в качестве входных данных исходный текст программы, решающей ту или иную задачу, и выполняет их проверку на наборе тестов, имеющихся для этой задачи в системе. Поскольку эта система используется для проведения зачетов и иных контрольных мероприятий, то в условии могут содержаться дополнительные требования по использованию (или не использованию) структур данных, операторов и т.п.

Разумеется, программа должна удовлетворять определенным требованиям по оформлению – в противном случае автоматизированная система может счесть решение неверным даже если оно по сути правильно.

Проект № 4. Решение задачи с числовым вводом.

В качестве примера рассмотрим следующую задачу (раздел «Для подготовки», задача 39):

Задача 39.

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

Ограничения: в последовательности содержится не более 10000 элементов

Формат входного файла input.txt

Строки, содержащие натуральные числа, разделенные пробелом. Последняя строка завершается нулем, который в последовательность не входит.

Формат выходного файла output.txt

Первая строка – целое число, количество элементов в выводимой последовательности.

Вторая строка – натуральные числа через пробел – элементы искомой подпоследовательности согласно условию задачи.

Примечание. После последнего числа пробела быть не должно.

Пример входного файла:

4 8 5

3 1 2 6 7 1 8

4 2

1 8 7 0

Пример выходного файла:

10

2 4 8 1 7 6 2 1 3 5

Задача отыскания максимума и минимума рассматривалась уже неоднократно. Однако здесь требуется найти наиболее удаленную друг от друга пару и (при необходимости) выбрать из пар, расстояние между которыми максимально, ту, которая удовлетворяет дополнительным условиям на расположение минимального, а при необходимости и максимального элемента в этой паре.

§ 1. Анализ условия задачи

«Наивный» подход к решению задачи состоит в том, чтобы сначала найти все максимумы и минимумы (т.е. все их номера в последовательности), затем составить из них все возможные пары и найти среди них наиболее удаленные друг от друга. Далее, если наиболее удаленных пар окажется несколько, потребуется найти среди них ту, в которой номер минимального элемента окажется наименьшим.

Однако такой подход может привести к очень долгим поискам решения. Действительно, рассмотрим «крайний» случай: в последовательности 10000 элементов, при этом элементами последовательности являются только числа a и b; для примера будем считать, что a < b и что тех и других в последовательности равное количество – т.е. по 5000. Тогда для каждого минимума может быть составлено 5000 пар (с каждым из максимумов). А поскольку и минимумов самих также 5000, то в итоге получаем 25 миллионов вариантов составления пар.

Поставим вопрос – можно ли сократить количество рассматриваемых вариантов? Нарисуем прямую и будем считать, что в точках прямой с целыми координатами расположены элементы последовательности. Отметим синими треугольниками те элементы, значения которых в данной последовательности минимальны, и красными – те, значения которых в данной последовательности максимальны (рис. 1).

Рис. 1. Минимумы и максимумы в последовательности.

Из рисунка становится очевидно, что рассматривать потребуется лишь пары, составленные из «крайних» вариантов: «самый левый минимум – самый правый максимум» и «самый правый минимум – самый левый максимум». Обосновать это просто. Предположим, что наиболее удаленная друг от друга пара имеет координаты iMin и iMax, причем iMin < iMax. Если существует максимум с координатой jMax, который находится правее максимума с координатой iMax, то расстояние между iMin и iMax не является максимальным, и они не могут быть самой удаленной друг от друга парой. Значит, если координата максимума больше координаты минимума, то это должен быть «самый

правый» максимум. Аналогичным образом заключаем, что минимум в этом случае должен быть «самым левым». В точности также можно рассмотреть случай iMin > iMax. Случай равенства iMin и iMax невозможен по условию задачи: среди элементов последовательности должны быть хотя бы два различных числа, и, значит, рассмотренные два случая (iMin < iMax и iMin > iMax) содержат в себе все возможные варианты. Если две этих пары окажутся равноудаленными, то выбрать нужно будет первую из них. Исключение составляет случай, когда минимум единственный, а пару максимумов слева и справа от него отделяет одинаковое количество элементов. В этой ситуации выбор нужно будет сделать в пользу «левого» максимума.

После того как пара «минимум – максимум» будет определена, нужно будет вывести «в обратном порядке все элементы последовательности между наибольшим и наименьшим элементами этой последовательности» (точная цитата из условия задачи). Это означает, что при выводе от наименьшего к наибольшему порядок будет прямым.

§ 2. Хранение данных

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

Память для массива должна быть выделена перед его использованием; изменять количество его элементов по ходу выполнения программы нельзя. Поэтому нам придется сразу выделить память для массива максимального размера (в условии задачи оговорено, что в последовательности не более 10000 элементов).

Поиск «самых левых» и «самых правых» минимумов и максимумов можно выполнить при чтении данных. Потребуется два целочисленных поля для хранения величин собственно минимума и максимума и четыре (два по два) поля для хранения «самых левых» и «самых правых» номеров. Заметим, что даже если минимум или максимум окажется единственным, это не повлияет на описанную в первом параграфе логику.

§ 3. Создание проекта

Создайте новый проект lab04_gp в папке W:\JavaProjects\LR04_01 и назовите его главный класс SampleTask. Если все остальные проекты будут закрыты, окно IDE будет иметь следующий вид (рис. 2).

Кроме того, потребуется создать входной файл с именем input.txt (как такое сделать, описывалось в лабораторной работе № 3) и заполнить его данными из примера. Впоследствии содержимое input.txt мы будем менять – с целью протестировать разработанную программу на других входных данных. Но начнем именно с тех, что предлагаются в условии задачи. Перенести данные в файл нужно в точности так же: с

соблюдением их расположения на строках (рис. 3). Перевод на следующую (пустую) строку во входном файле является «стандартом» для файлов в проверяющей системе.

Примечание.

Компилятор Java вполне может обойтись без этого перевода строки, но, поскольку проверяющая система работает и с другими языками, входные файлы хранятся в том виде, в

котором их смогут успешно обработать все используемые в системе компиляторы.

Рис. 2. Новый проект в среде NetBeans

Рис. 3. Входной файл для тестирования проекта

Класс SampleTask будет, как обычно, просто «запускающим» – его единственный метод main() будет отвечать за создание экземпляра другого класса и вызов его методов. Класс, в котором будет содержаться «логика», собственно решение задачи, назовем class Task39 (по номеру задачи) и опишем в нем поля, оговоренные в параграфе 2 (рис. 4).

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

Рис. 4. В классе Task39 описаны поля для хранения последовательности, а также минимумов и максимумов

Опишите конструктор и объект класса Scanner в нем для чтения из файла (рис. 5). Воспользуйтесь подсказками среды, чтобы добавить предложения импорта и декларацию throws для пропуска исключения (рис. 6).

Рис. 5. Объект класса Scanner для чтения из файла

Отметим, что при создании объекта класса Scanner мы не стали создавать отдельный объект класса FileReader – его создание было выполнено непосредственно в конструкторе класса Scanner. Это лишает нас возможности обращаться к объекту класса FileReader «отдельно», но для наших целей это и не нужно.

Рис. 6. Добавленные предложения импорта

.

§ 4. Чтение и обработка данных

Теперь выделим память для массива. Конечно, можно просто написать: element = new int [10000], но использование «магических чисел» не есть признак «хорошего тона». Даже если число (как представляется сейчас) используется в программе единожды, нет гарантии, что это не потребуется сделать еще раз. Поэтому вместо числовых обозначений рекомендуется вводить специальную константу. Пометить поле как неизменяемое можно, снабдив его модификаторами static и final.

Если поле помечено как final, оно может быть инициализировано один раз – либо при описании поля (т.е. инициализации класса), либо в конструкторе (т.е. при инициализации объекта). Если же добавляется модификатор static, то единственной возможностью инициализации остается инициализация при описании поля (поскольку static означает, что все экземпляры класса должны разделять одно и то же значение и, следовательно, оно должно получать значение при инициализации класса). Мы применим оба модификатора (скорее в учебных целях, чем в силу необходимости) (рис. 7).

Рис. 7. Объявление константы и использование ее при распределения памяти для массива

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

Примечание.

Поскольку, по условию задачи, числа в последовательности натуральные, то в качестве начального значения максимального элемента можно было бы выбрать 0. Минимум же допустимо

инициализировать, например, значением Integer.MAX_VALUE (231 – 1). Эти значения неизбежно будут заменены после прочтения элементов последовательности.

Рис. 8. Пока «левые» и «правые» максимумы и минимумы совпадают

Затем логика действий будет такова:

прочитать очередное число из входного файла

если оно не равно нулю, записать его в очередной элемент массива

сравнить его с минимумом

если число оказалось меньше минимума, то обновить значения как собственно минимума, так и «левой», и «правой» позиции минимума в последовательности

если число оказалось равно минимуму, то обновить только значение «правой» позиции минимума в последовательности

если число оказалось больше минимума, имеет смысл сравнить его с максимумом. Если число оказалось больше максимума, то обновить значения как максимума, так и «левой» и «правой» позиции максимума в последовательности

если число оказалось равно максимуму, то обновить только значение «правой» позиции максимума в последовательности

если же число находится между текущими значениями минимума и максимума, ничего делать не нужно.

увеличить номер элемента массива, в который будет записан очередной элемент последовательности.

Обратите внимание на запись условия цикла while. В нем совмещено чтение очередного элемента в переменную el и ее последующее сравнение с нулем.

По завершении цикла массив elements будет заполнен членами последовательности, причем значение i будет равно количеству членов последовательности и номеру первого «свободного» элемента массива. Переменные Min и Max будут содержать значение минимального и максимального элемента последовательности соответственно, а iLeftMin, iLeftMax, iRightMin и iRightMax – номера соответствующих элементов массива.

Рис. 9. Чтение и анализ элементов последовательности

§ 5. Определение наиболее удаленных друг от друга минимума и максимума

Задачей следующего метода, который мы разработаем, будет определение наиболее удаленных друг от друга минимума и максимума и вывод элементов между ними в порядке, указанном в условии задачи.

Как уже обсуждалось выше, нам достаточно будет сравнить две пары «минимум – максимум». Поскольку при «прочих равных» пара, содержащая «левый минимум» предпочтительнее, то и сравнение организуем соответствующим образом (рис. 10).

Переменные startIndex и stopIndex определяют, с какого по какой элемент следует выводить, учитывая, что сами максимум и минимум, ограничивающие фрагмент массива, выводить не следует. Переменная outputDirection указывает направление вывода – в сторону увеличения (1) или в сторону уменьшения (–1) номеров элементов массива. Рекомендуется удостовериться, что добавление и вычитание единиц выполнено корректно (проще всего изобразить соответствующие ситуации на рисунке).

Условие составлено так, чтобы пара «правый» максимум и «левый» минимум имела преимущество при прочих равных (напомним, что в условии задачи сказано, что для равноудаленных пар следует выбрать ту, в которой минимум ближе к началу последовательности – т.е. левее, а при совпадении этого параметра выбрать ту, в которой максимум ближе к концу последовательности – т.е. правее).

Чтобы использовать метод abs класса Math без указания класса, его следует импортировать статически (рис. 11).

Рис. 10. Выбор фрагмента массива для вывода

Рис. 11. Статический импорт Math.abs()

Чтобы вывести результаты в файл output.txt, опишем объект fout класса PrintWriter и передадим имя файла в качестве параметра его конструктору. Заметим, что заголовок метода потребуется дополнить предложением throws (рис. 12).

Примечание.

Конечно, именно в таком контексте – создания нового файла – исключение FileNotFound

возникнуть не может. Однако объект класса PrintWriter может использоваться не только для перезаписи, но и для добавления информации в существующий файл. Поэтому разработчики обязывают программистов предусматривать такое исключение.

Рис. 12. Предложение throws предложит написать IDE

В первой строке файла следует вывести количество элементов массива, расположенных между наиболее удаленными минимумом и максимумом. Это количество легко посчитать, когда известно значение переменных startIndex и stopIndex (рис. 13).

Рис. 13. Вывод первой строки в файл output.txt

Чтобы обеспечить отсутствие пробела после последнего выводимого элемента массива, придется этот последний элемент выводить вне цикла (рис. 14). Конечно, это не единственно возможное решение проблемы: можно, например, отдельно вывести первый элемент или же использовать более хитрые приемы.

Рис. 14. Вывод элементов массива в файл

Примечание.

Оговоримся сразу – в написанном методе есть логическая ошибка. Если она Вам очевидна

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

Рис. 15. Метод main() главного класса

Соседние файлы в предмете Программирование на Java