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

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

5

БЛАГОДАРНОСТИ

На данный труд подтолкнули меня мои ученики, которых я готовил к олимпиадам. Однажды мне надоело объяснять и повторять одно и тоже разным участникам моего кружка спортивного программирования, и так в ноябре 2010 года появилась первая редакция этого руководства. В процессе совершенствования знаний и навыков моих учеников, они сами стали подсказывать мне и друг другу различные приёмы кодирования решений. Некоторые из них были внесены в это руководство. В числе этих учеников хочу назвать прежде всего Егора Литвиненко, Сергея Косивченко и Александра Гончарова.

ОСНОВЫ

В этом разделе рассматриваются вопросы, относящиеся к самым азам не только спортивного программирования, но и вообще программирования на языке C++, без знания которых не стоит даже приближаться к местам, где проходят соревнования. :)

ПРИВЕТ, МИР!

Как же без приветствия Миру?

Ниже представлен код программы “Hello, World!” в стиле C++ и C:

C++

C

#include <iostream>

#include <cstdio>

using namespace std;

int main(int argc, char* arv[]) {

int main(int argc, char* argv[]) {

puts("Hello, World!");

return 0;

cout << "Hello, World!" << endl;

return 0;

}

 

}

 

 

 

В решениях олимпиадных задач список аргументов функции main() можно опускать.

(На самом деле пример “Hello, World!” это не дань традиции, а способ показать примерный вид программы на C++, написанной в двух разных стилях.)

ВВОД-ВЫВОД

Ввод-вывод на C++ можно осуществлять в объектно-ориентированном стиле и в процедурном стиле, унаследованном из C. Объектно-ориентированный стиль как-правило более удобен и требует набора меньшего количества букв более простой в использовании, но второй потребляет меньше процессорного времени при интенсивном вводе-выводе.

При программировании в стиле C++ средства ввода-вывода подключаются заголовком iostream, за ввод отвечает объект cin, за вывод – cout. А в стиле C средства ввода-вывода подключаются заголовком cstdio, За ввод-вывод отвечает большое количество функций, но универсальными являются две: функция scanf() для ввода и printf() для вывода. Первым аргументом этих функций является так называемая строка формата. Строка формата для целых чисел должна содержать %d или %i, для символов – %c и для C-строк – %s. Следует также знать и не забывать, что функция printf() не делает автоматического переноса на новую строку. Для осуществления переноса включайте в конец строки формата символ \n.

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

Ниже представлен примеры программ в объектно-ориентированном и процедурном стилях, читающие со стандартного ввода два целых числа и выводящие их сумму.

6

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

 

 

 

C++

 

C

 

 

 

#include <iostream>

 

#include <cstdio>

using namespace std;

 

int main(int argc, char* argv[]) {

int main(int argc, char* argv[])

int a = 0, b = 0;

scanf("%d", &a);

{

 

scanf("%d", &b);

int a = 0, b = 0;

 

int c = a + b;

cin >> a >> b;

 

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

int c = a + b;

 

return 0;

cout << c << endl;

 

return 0;

 

}

 

 

}

 

 

 

 

 

Если нужно данные читать из файла и результат также выводить в файл, то вместо непосредственной работы с файлами, можно просто перенаправить на них стандартные потоки ввода (stdin) и вывода (stdout) с помощью функции C freopen(). В обоих примерах ниже из файла input.txt читаются два числа, а результат их сложения выводится в файл output.txt.

C++

C

#include <iostream>

#include <cstdio>

using namespace std;

int main(int argc, char* argv[]) {

int main(int argc, char* argv[])

freopen("input.txt", "r", stdin);

freopen("output.txt", "w", stdout);

{

int a = 0, b = 0;

freopen("input.txt", "r", stdin);

freopen("output.txt", "w", stdout);

scanf("%d", &a);

int a = 0, b = 0;

scanf("%d", &b);

int c = a + b;

cin >> a >> b;

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

int c = a + b;

return 0;

cout << c << endl;

return 0;

}

 

}

 

 

 

Другие вопросы ввода-вывода рассматриваются в других разделах.

ЦЕЛЫЕ ЧИСЛА

В таблице ниже представлены все основные целочисленные типы C++. Так как в C++ размеры целочисленных типов относительны, то есть зависят от архитектуры, то здесь указаны размеры для современных 32 и 64 разрядных архитектур.

Обозначение

Размер

Комментарий

 

 

 

char

1

По совместительству символьный тип (для 8 разрядных кодировок).

 

 

 

short int

2

Можно использовать более короткое обозначение short.

 

 

 

int

4

«Родной» тип для 32 разрядных архитектур.

long int

4

Эквивалентен int. Можно использовать более короткое обозначение

 

 

long.

long long int

8

«Родной» тип для 64 разрядных архитектур. На 32 разрядных

 

 

эмулируется. Можно использовать более короткое обозначение

 

 

long long.

Все перечисленные типы являются знаковыми, то есть допускают как положительные числа, так и отрицательные. В некоторых случаях целесообразно использование беззнаковых целых переменных и констант. В таком нужно к обозначению типа добавлять слово unsigned. Например, unsigned int. Не следует применять unsigned для расширения диапазона. Беззнаковость применяется в

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

7

тех случаях, когда отрицательные числа являются недопустимыми. Например, размер объектов класса vector или длина строки являются такими случаями. Типовая ошибка: объявляется в операторе for индексная переменная с типом int, то есть знаковая, которая в нём сравнивается с результатом метода size() или length(), который является беззнаковым. В этом случае следовало бы объявить индексную переменную с типом size_t, который для этого как раз и предназначен, или воспользоваться синонимом типа для размера из соответствующего класса. (На 32 разрядных компьютерах тип size_t фактически является синонимом для unsigned int и его диапазон 0 .. 4 294 967 295.)

В таблице ниже представлены диапазоны основных целочисленных типов 32 и 64 разрядных компьютеров.

Тип

 

Диапазон

 

 

char

–128 .. 127

unsigned char

0

.. 255

 

 

short int

–32 768 .. 32 767

 

 

 

unsigned short

0

.. 65 535

 

 

int

–2 147 483 648 .. 2 147 483 647

 

 

 

unsigned int

0

.. 4 294 967 295

long int

–2 147 483 648 .. 2 147 483 647

 

 

 

unsigned long int

0

.. 4 294 967 295

 

 

long long int

–9 223 372 036 854 775 808 .. 9 223 372 036 854 775 807

 

 

unsigned long long int

0..18 446 744 073 709 551 615

 

 

 

Следующая программа позволит читателю проверить на своём компьютере диапазон значений основных целых типов:

#include <iostream> #include <limits>

using namespace std;

int main(int argc, char* argv[]) { cout << "char: "

<<(int)numeric_limits<char>::min() << ".."

<<(int)numeric_limits<char>::max() << endl;

cout << "unsigned char: "

<<(int)numeric_limits<unsigned char>::min() << ".."

<<(int)numeric_limits<unsigned char>::max() << endl; cout << "wchar_t: "

<<numeric_limits<wchar_t>::min() << ".."

<<numeric_limits<wchar_t>::max() << endl;

cout << "short: "

<<numeric_limits<short>::min() << ".."

<<numeric_limits<short>::max() << endl; cout << "unsigned short: "

<<numeric_limits<unsigned short>::min() << ".."

<<numeric_limits<unsigned short>::max() << endl;

cout << "int: "

<<numeric_limits<int>::min() << ".."

<<numeric_limits<int>::max() << endl; cout << "unsigned int: "

<<numeric_limits<unsigned int>::min() << ".."

<<numeric_limits<unsigned int>::max() << endl;

cout << "long: "

<<numeric_limits<long>::min() << ".."

<<numeric_limits<long>::max() << endl; cout << "unsigned long: "

<<numeric_limits<unsigned long>::min() << ".."

8

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

<<numeric_limits<unsigned long>::max() << endl; cout << "long long: "

<<numeric_limits<long long>::min() << ".."

<<numeric_limits<long long>::max() << endl;

cout << "unsigned long long: "

<<numeric_limits<unsigned long long>::min() << ".."

<<numeric_limits<unsigned long long>::max() << endl; cout << "__int64: "

<<numeric_limits<__int64>::min() << ".."

<<numeric_limits<__int64>::max() << endl;

cout << "size_t: "

<<numeric_limits<size_t>::min() << ".."

<<numeric_limits<size_t>::max() << endl;

return 0;

}

В этом коде используется шаблонный класс numeric_limits<> из <limits>.

Чаще всего подходящим типом целых чисел является int. Но часто встречает такая ситуация: исходные значения прекрасно помещаются в int, но в процессе вычислений результат выходит за пределы диапазона int. Это одна из типовых ошибок начинающих. Выходом является использование целого типа long long int, который является 64-разрядным.

Другая типовая ошибка возникает в ситуации, которую мы рассмотрим на простом примере:

int x = 1000000000, y = 1000000000; long long int z = x * y;

cout << z << endl;

Результат неожиданный: –1486618624. Оказывается, механизм приведения типов языка C++ таков, что типом (размером) результата является тип выражения. И действительно, типом выражения в нашем примере является int, так как оба множителя имеют тип int.

Проблема решается принудительным приведением типов переменных, составляющих выражение:

int x = 1000000000, y = 1000000000; long long int z = (long long int) x * y; cout << z << endl;

Теперь мы получаем ожидаемый результат: 1000000000000000000.

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

Некоторые спортсмены вместо long long используют __int64.

Для того, чтобы компиляторы правильно обрабатывали длинные целочисленные литералы со знаком нужно использовать суффикс LL, например, –2000000000LL. Использование вместо этого приведение типа (long long) не приводит к ожидаемому результату.

СИМВОЛЫ И СТРОКИ

В C++ базовым символьным типом является тип char. Данные этого типа занимают 1 байт и предназначены для хранения отдельных символов в 8-разрядной кодировке, обычно ASCII. Тип является знаковым и в числовом представлении его диапазон от –128 до 127. Константы этого типа, то есть символы, должны заключаться в одинарные кавычки. Символы упорядочены в соответствие с кодировкой и их можно сравнивать между собой.

Для чтения отдельных символов со стандартного ввода можно использовать функцию scanf() с форматом %c, но лучше – getchar():

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

9

char c; scanf("%c", &c); c = getchar();

Для вывода отдельных символов на стандартный вывод можно использовать функцию printf() с форматом %c, но лучше – putchar():

char c; printf("%c", c); putchar(c);

Строки в C++ могут быть двух видов. Первый вид – это так называемые C-строки, унаследованные из языка C. Второй вид строк – это объектно-ориентированные строки типа string.

C-строка представляет из себя массив символов типа char с дополнительным элементом в конце для хранения ограничителя строки, в качестве которого выбрано число 0 (оно же символ '\0'). Недостатком C-строк является то, что часто мы не знаем заранее длину строки (количество её символов) и место под неё нужно резервировать по максимуму, а это не всегда подходит.

Для ввода C-строк используются функции scanf() с форматом %s и gets(). Вторая должна работать быстрее, что может оказаться критически важным при чтении большого количества строк:

char s[5]; scanf("%s", &s); gets(s);

Для вывода C-строки можно использовать функцию printf() с форматом %s, но лучше – puts(), только нужно не забывать о том, что puts() после вывода строки делает переход на новую строку. Если это не то, что нам нужно, следует использовать printf().

char s[5]; printf("%s\n", s); printf(s); puts(s);

Включив заголовок cstring мы получаем доступ к функциям библиотеки C для C-строк:

Название

Назначение

Сигнатура

Возвращаемое значение

 

 

 

 

strcat()

Конкатена-

char* strcat(char* dst, const char* src);

Указатель на строку-приёмник dst.

 

ция

двух

 

 

 

 

 

 

строк.

 

 

 

 

 

strcmp()

Сравнение

int strcmp(const char* str1, const char* str2);

Число 0, если строки посимвольно

 

двух строк.

 

совпадают; число большее 0, если

 

 

 

 

в первой строке обнаружен символ

 

 

 

 

больший

 

соответствующего

 

 

 

 

символа второй; и меньшее 0 в

 

 

 

 

противном случае.

 

strcpy()

Копирова-

char* strcpy(char* dst, const char* src);

Указатель на строку-приёмник dst.

 

ние строки.

 

 

 

 

 

strlen()

Длина стро-

size_t strlen(const char* str);

Длина

строки

(количество

 

ки (количе-

 

символов)

без учёта символа

 

ство

симво-

 

ограничителя (\0).

 

 

лов).

 

 

 

 

 

 

strstr()

Поиск

char* strstr(const char* str1, const char* str2);

Указатель

на

первое

вхождение

 

подстроки.

 

str2 в str1. Если str2 не найдена в

 

 

 

 

str1, то возвращается нулевой

 

 

 

 

указатель.

 

 

 

strtok()

Разбиение

char* strtok(char* str, const char* delimiters);

Указатель на подстроку из str,

 

строки.

 

обрамлённую

одним

или более

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