Здесь собран материал, который относится не к конкретным алгоритмам, а к олимпиадному программированию в целом.
Обычно написание решения олимпиадной задачи начинается с кодирования операций с файлами. Мы поясним это на примере простой задачи - на входе даются два числа A и B, надо вывести их сумму.
Самое очевидное решение этой задачи выглядит так:
program Sum; var A,B : Integer; begin Assign(Input,'input.txt'); Reset(Input); Assign(Output,'output.txt'); Rewrite(Output); Read(A,B); Writeln(A+B); Close(Input); Close(Output); end. |
Заметьте, что в этом примере не объявляются файловые переменные, так как операции ввода/вывода проводятся через стандартные файлы Input и Output, которые есть в любой реализации Паскаля. Это позволяет также не указывать имя файловой переменной в вызовах функций Read и Write. Кроме того, в Турбо Паскале последние две строки кода можно опустить, так как в нем файлы Input и Output автоматически закрываются в конце выполнения программы. (К сожалению, к Free Pascal это не относится). Наконец, если вы хотите тестировать программу для ввода с клавиатуры, то достаточно закомментировать строки, связанные с вызовами Assign(), Reset(), Rewrite() и Close(), и не требуется изменять инструкции чтения/записи.
В реализации более сложных задач может потребоваться разделить процесс выполнения на три: ввод, решение и вывод. В этом случае решение задачи "A+B" будет выглядеть так:
program Sum; var A,B,C : Integer; procedure Inp; begin Assign(Input,'input.txt'); Reset(Input); Assign(Output,'output.txt'); Rewrite(Output); end; procedure Run; begin C := A+B; end; procedure Outp; begin Writeln(C); Close(Input); Close(Output); end; begin Inp; Run; Outp; end. |
Реализация таким способом решения задачи "A+B" может показаться странной, но для более сложных задач она часто бывает удобнее. Кроме, того, она позволяет использовать разные переменные цикла для чтения, решения и записи и не делать их глобальными.
Время работы алгоритмов сильно зависит от языка программирования, процессора, операционной системы. Поэтому для оценки времени работы (cложности) алгоритма применяется асимптотическая оценка - говорят, что алгоритм работает за время O(f(x)), где x - переменная, связанная с размером входных данных, а f(x) - некоторая функция. Например, алгоритм обхода в глубину работает за время O(N^2), где N - число вершин графа. Что же означает символ O?
Пусть функции f(x) и g(x) больше нуля при x>0. f(x) называется O большим от g(x) (f(x)=O(g(x))), если найдется такие числа C>0 и x0>0, что f(x)<Cg(x) при всех x>x0. Обозначение f(x)=O(g(x)) фактически означает, что порядок функции f(x) при больших x не больше, чем функции g(x). Ясно, что f(x)=O(f(x)) и из f(x)=O(g(x)) и g(x)=O(h(x)) следует f(x)=O(h(x)).
Приведем несколько примеров: x=O(x^2), x^2=O(x^3), log x=O(x), ax=O(x), log(x!)=O(x log x).
Соотношение с O показывает, как быстро будет расти время работы алгоритма при больших x, что весьма важно. Пусть, например, у нас есть два алгоритма, один из которых работает за время f(x)=x^2, а второй за время g(x)=10000x. Ясно, что f(x)=O(x^2), g(x)=O(x). При малых x f(x) будет намного меньше g(x), но при больших x, например при x=100000, f(x)=10^10, а g(x)=10^9<f(x), и при увеличении x разница между этими двумя функциями будет быстро расти.
В большинстве задач есть ограничение по времени, и в случае его превышения за тест ставится 0 баллов, даже если программа в процессе перебора уже нашла правильный ответ. В таких случаях применяется отсечение по времени - в начале выполнения программы записывается текущее время, и перебор прерывается в том случае, если время выполнения программы превышает критическое значение; в этом случае выводится наилучший полученный к этому моменту результат.
В Турбо Паскале для реализации отсечения по времени используется переменная типа Longint по абсолютному адресу $0:$46c; ее значение увеличивается на единицу каждые 55 миллисекунд:
program TheTime_TP; const CriticalTime = 4.8; {seconds} var Ct : Longint absolute 0:$46c; Ot : Longint; begin Ot := Ct + CriticalTime*1000/55; ... while ... do begin if Ct>Ot then Break; ... end; ... end. |
В Free Pascal для реализации отсечения по времени используется функция Now() из модуля SysUtils; она возвращает текущее время и дату в формате TDateTime; Now() становится больше на единицу через 1 сутки:
program TheTime_FP; uses SysUtils; const CriticalTime = 4.8; {seconds} CriticalD : TDateTime = CriticalTime/(24*60*60); var Ot : TDateTime; begin Ot := Now + CriticalTime; ... while ... do begin if Now>Ot then Break; ... end; ... end. |
© Semyon Dyatlov, 2002 | Пишите мне |