Добавил:
Upload Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:
recipes.pdf
Скачиваний:
34
Добавлен:
22.05.2015
Размер:
322.44 Кб
Скачать

Симоненко Евгений А. Олимпиадная подготовка по программированию

21

cout << endl;

}

}

delete[] a;

return 0;

}

В этом способе мы резервируем непрерывный кусок памяти размера n x m, а потом с помощью макроса имитируем работу с двумерным массивом. Обратите внимание на скобки вокруг i и j. Они необходимы для избежания трудноуловимых ошибок в случаях, когда i или j являются выражениями.

ЭЛЕМЕНТАРНЫЕ АЛГОРИТМЫ

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

АБСОЛЮТНОЕ ЗНАЧЕНИЕ ЦЕЛОГО ЧИСЛА

Функции вычисления абсолютного значения числа, в том числе и с плавающей точкой, подключаются с помощью заголовка cmath. Проблема может возникнуть при вычислении абсолютного значения целого числа с компилятором G++ (GCC для C++). В библиотеке C, идущей вместе с ним, просто нет соответствующей функции. Но это не беда. Абсолютное значение целого числа вычисляется очень просто:

x >= 0 ? x : -x

Если вычисление абсолютного значения встречается в коде несколько раз, то лучше написать in- line-функцию:

inline int abs(int x) {

return (x >= 0 ? x : -x);

}

МИНИМУМ И МАКСИМУМ СРЕДИ ДВУХ ЧИСЕЛ

Пусть a и b целые числа, тогда минимум будем вычисляться следующим выражением:

a < b ? a : b

А максимум – следующим:

a > b ? a : b

Соответствующие inline-функции:

inline int min(int a, int b) { return (a < b ? a : b);

}

inline int max(int a, int b) { return (a > b ? a : b);

}

Но, вообще говоря, в стандартной библиотеке C++ уже есть соответствующая функция, которая является шаблонной и может использоваться и с другими типами, а не только int, в частности, c типами char и double.

МИНИМУМ И МАКСИМУМ СРЕДИ ТРЁХ ЧИСЕЛ

Пусть a, b и c – целые числа, тогда минимум будем вычисляться следующим выражением:

a < b ? (a < c ? a : c) : (b < c ? b : c)

22

Симоненко Евгений А. Олимпиадная подготовка по программированию

А максимум – следующим:

a > b ? (a > c ? a : c) : (b > c ? b : c)

Если внимательно посмотреть на эти выражения, то можно заметить, что в них вычисляется min и max, соответственно для двух чисел, то есть задача поиска минимума и максимума среди трёх чисел сводится к вычислению минимума и максимуму, соответственно, для двух чисел:

min(min(a,b),c)

max(max(a,b),c)

Этот же подход нетрудно применить и на нахождение минимума и максимума среди четырёх чисел (это также довольно часто встречающаяся проблема).

Ну и при необходимости можно написать соответствующие inline-функции:

inline int min3(int a, int b, int c) { return min(min(a,b),c);

}

inline int max3(int a, int b, int c) { return max(max(a,b),c);

}

СОРТИРОВКА МАССИВА ИЗ ТРЁХ ЧИСЕЛ

Пусть x – массив трёх целых чисел, тогда сортировку его по возрастанию можно сделать так:

if (x[0] > x[1]) { int t = x[0]; x[0] = x[1]; x[1] = t;

}

if (x[1] > x[2]) { int t = x[1]; x[1] = x[2]; x[2] = t;

}

if (x[0] > x[1]) { int t = x[0]; x[0] = x[1]; x[1] = t;

}

ЦИКЛИЧЕСКИЙ СДВИГ МАССИВА ИЗ ТРЁХ ЭЛЕМЕНТОВ

Пусть x – массив трёх элементов, тогда циклический сдвиг его влево можно сделать так:

int t = x[0]; x[0] = x[1]; x[1] = x[2]; x[2] = t;

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

РАЗЛОЖЕНИЕ ЦЕЛОГО ЧИСЛА НА ЕГО ЦИФРЫ

Пусть n – целое число, а k – основание системы счисления, тогда его цифры в этой системе счисления можно получить так:

n = abs(n); do {

char d = n % k;

cout << char(d + '0'); n /= k;

} while (n != 0); cout << endl;

Симоненко Евгений А. Олимпиадная подготовка по программированию

23

Сначала с помощью получения остатка от деления на основание системы счисления мы получаем самую младшую цифру, затем целочисленным делением на основание системы мы удаляем эту самую младшую цифру. Число n будет в каждой итерации уменьшаться, пока не станет равным 0. Цифры будут выведены в обратном порядке.

ЛИНЕЙНЫЙ ПОИСК

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

1)поиска индексов элементов с заданным значением (часто ищется только первый или последний подходящий элемент);

2)для проверки, есть ли вообще в массиве элемент с таким значением;

3)для подсчёта того, сколько раз встречается заданное значение среди элементов массива;

4)для поиска элемента с ближайшим к заданному значению;

5)для поиска значения и/или индекса элемента с максимальным или минимальным значением.

Вхудшем случае потребуется перебрать все элементы массива, например, при поиске максимума или минимума. Сложность алгоритма O(n). Для упорядоченных массивов более целесообразным является использование алгоритма бинарного поиска.

Рассмотрим пример линейного поиска на примере задачи поиска элемента с ближайшим к заданному значению. Пусть x – значение, для которого ищется ближайший по значению элемент в массиве a.

int res = a[0]; if (res == x) {

printf("%d\n", res); return 0;

}

for (int i = 1; i < n; i++) {

if (abs(a[i] - x) < abs(res - x)) { res = a[i];

}

}

printf("%d\n", res);

Линейный поиск элемента с заданным значением можно ускорить, добавив к массиву ещё один элемент, который принято называть барьерным, и значение которого будет совпадать с заданным. Это позволит избавиться от проверки выхода за пределы массива в каждой итерации, так как если элемент в массиве не будет найден, то он всё-равно совпадёт по значению с барьерным. Таким образом признаком того, что элемент не был найден, будет совпадение индекса с длиной исходного массива.

Пусть n – количество элементов массива, x – сам массив, а y – заданное значение для поиска, тогда код поиска с барьером может выглядеть так:

#include <cstdio>

int main(int argc, char* arv[]) { int n = 0;

scanf("%d", &n);

int *x = new int[n + 1];

for (int i = 0; i < n; i++) { scanf("%d", &x[i]);

}

int y = 0; scanf("%d", &y);

24 Симоненко Евгений А. Олимпиадная подготовка по программированию

x[n] = y; int res = 0;

for (int i = 0; ; i++) { if (x[i] == y) {

res = i; break;

}

}

puts(res == n ? "NO" : "YES");

return 0;

}

Не забывайте при использовании поиска с барьером вызывать break при нахождении элемента.

Разновидностью барьерного поиска можно считать линейный поиск в C-строках. В самом деле, C- строки, являясь обыкновенными массивами символов, в тоже время всегда имеют последним элементом символ с кодом 0 ('\0'), который можно рассматривать как барьерный элемент. Например, код проверки на вхождение в состав C-строки s некоторого символа c может быть таким:

bool found = false;

for (int i = 0; s[i]; i++) { if (s[i] == c) {

found = true; break;

}

}

Использование барьера целесообразно в следующих задачах: «Время сериала!» (http://codeforces.ru/contest/60/problem/B),

“3D City Model” (http://acm.sgu.ru/problem.php?contest=0&problem=519).

Многие алгоритмы для перечисленных выше проблем реализованы как часть стандартной библиотеки языка C++, например, min_element для нахождения минимального элемента, max_element – максимального, find для нахождения позиции элемента с заданным значением. Рекомендуется познакомиться с алгоритмами из STL и научиться ими пользоваться.

РЕКУРСИЯ

Метод рекурсии хоть и не часто, но всё же встречается в решениях олимпиадных задач. В некоторых случаях нужно догадаться использовать в решении рекурсию, например, в решении задачи «Время сериала!» (http://codeforces.ru/contest/60/problem/B), некоторые задачи могут быть решены как с её помощью, так и без неё, например, задача вычисления факториала (см. ниже), а рекурсивное решение некоторых прямо вытекает из их условий, например, такова задача “Wiki Lists” с 13го четвертьфинала, проводившегося в Саратове в 2009 году (http://acm.sgu.ru/problem.php? contest=0&problem=461).

Рассмотрим рекурсию на одном из простейших примеров, на задаче вычисления факториала. Факториалом целого числа n называется произведение всех целых чисел от 1 до n:

n

n !=i

i=1

Для n = 0 факториал определяется равным 1:

0!=1

Очевидное решение – итерации (цикл):

int f(int n) {

if (n == 0) { return 1;

}

int res = 1;

Соседние файлы в предмете [НЕСОРТИРОВАННОЕ]