Настройка CODING-задач
CODING — это тип задач для алгоритмических соревнований. Участники пишут код, который автоматически проверяется на наборе тестов. В этом руководстве — всё о создании и настройке таких задач.
Обзор
Как работает проверка
1. Участник отправляет код
2. Код компилируется (для C++, Java, и т.д.)
3. Программа запускается на каждом тесте
4. Вывод сравнивается с ожидаемым (через чеккер)
5. Результаты возвращаются участнику
Компоненты CODING-задачи
| Компонент | Описание | Обязательно |
|---|---|---|
| Условие | Текст задачи с описанием | Да |
| Тесты | Пары вход/выход | Да |
| Ограничения | Время и память | Да |
| Языки | Разрешённые языки программирования | Да |
| Чеккер | Программа для проверки ответа | Нет (стандартный) |
| Интерактор | Для интерактивных задач | Нет |
Создание CODING-задачи
Шаг 1: Создайте задачу
- Перейдите в «Треки» → выберите трек
- Нажмите «Управление задачами»
- Нажмите «+ Добавить задачу»
- Выберите тип CODING
Шаг 2: Заполните основные поля
| Поле | Описание | Пример |
|---|---|---|
| Название | Название задачи | «Сумма чисел» |
| Условие | Описание задачи (Markdown) | Полный текст условия |
| Баллы | Максимальный балл за задачу | 100 |
| Порядок | Позиция в треке | 1 |
Шаг 3: Настройте ограничения
| Настройка | Описание | Рекомендации |
|---|---|---|
| Время (мс) | Лимит времени на один тест | 1000-5000 мс |
| Память (МБ) | Лимит памяти | 256-512 МБ |
Важно: Ограничения применяются к каждому тесту отдельно. Если тестов 10 и лимит 1 сек, общее время может быть до 10 секунд.
Шаг 4: Выберите языки
- В разделе «Языки программирования» выберите доступные языки
- Укажите язык по умолчанию (будет выбран при открытии)
- Опционально: отключите выбор языка (если разрешён только один)
Шаг 5: Добавьте тесты
Подробнее в разделе Тесты ниже.
Шаг 6: Сохраните задачу
Нажмите «Создать» или «Сохранить».
Ограничения по времени и памяти
Как вычисляются ограничения
| Параметр | Описание |
|---|---|
| Время | Время CPU, не реальное время. Включает только выполнение кода пользователя |
| Память | Пиковое использование памяти процессом (heap + stack) |
Рекомендации по настройке
| Тип задачи | Время | Память | Комментарий |
|---|---|---|---|
| Простая | 1 сек | 256 МБ | Линейные алгоритмы |
| Средняя | 2-3 сек | 256 МБ | O(N log N) алгоритмы |
| Сложная | 5 сек | 512 МБ | Тяжёлые вычисления |
| Строки | 2-3 сек | 512 МБ | Работа с большими строками |
Учёт особенностей языков
Разные языки имеют разную скорость выполнения:
| Язык | Относительная скорость | Рекомендация |
|---|---|---|
| C/C++ | 1x (базовая) | Эталон для расчёта лимитов |
| Java | 2-3x медленнее | Увеличьте лимит в 2-3 раза |
| Python | 10-50x медленнее | Увеличьте лимит в 10+ раз |
| JavaScript | 3-5x медленнее | Увеличьте лимит в 3-5 раз |
Совет: Если задача рассчитана на C++, но разрешён Python, установите более мягкий лимит или используйте разные лимиты для разных языков.
Тесты
Структура теста
Каждый тест состоит из:
| Компонент | Описание |
|---|---|
| Входные данные (input) | Что получает программа на stdin |
| Ожидаемый вывод (output) | Что должна вывести программа |
| Баллы | Очки за этот тест (опционально) |
Публичные vs скрытые тесты
| Тип | Видны участнику | Назначение |
|---|---|---|
| Публичные | Вход и выход | Примеры в условии |
| Скрытые | Только результат | Основная проверка |
Добавление тестов
Вручную
- В редакторе задачи перейдите в раздел «Тесты»
- Нажмите «+ Добавить тест»
- Заполните:
- Входные данные — текст или загрузите файл
- Ожидаемый вывод — текст или загрузите файл
- Публичный — поставьте галочку для примеров
- Баллы — если включён режим частичных баллов
Из файлов
Для большого количества тестов:
- Подготовьте файлы с именами:
01.in, 01.out 02.in, 02.out ... - Заархивируйте в ZIP
- Загрузите через «Импорт тестов»
Рекомендации по тестам
| Категория | Описание | Пример |
|---|---|---|
| Граничные случаи | Минимальные/максимальные значения | N=1, N=10^9 |
| Особые случаи | Нули, отрицательные, пустые | N=0, arr=[] |
| Случайные | Средние случайные данные | Сгенерированные |
| Максимальные | Стресс-тесты на производительность | Максимальный N |
Генерация тестов
Для автоматической генерации используйте скрипт:
import random
def generate_test(test_num, n_min, n_max):
n = random.randint(n_min, n_max)
arr = [random.randint(1, 10**9) for _ in range(n)]
# Входные данные
with open(f'{test_num:02d}.in', 'w') as f:
f.write(f'{n}\n')
f.write(' '.join(map(str, arr)) + '\n')
# Ожидаемый вывод (ваш эталонный алгоритм)
result = solve(n, arr)
with open(f'{test_num:02d}.out', 'w') as f:
f.write(f'{result}\n')
# Генерация 20 тестов
for i in range(1, 21):
generate_test(i, 1, 10**5)
Чеккер (Checker)
Что такое чеккер
Чеккер — это программа, которая проверяет правильность ответа участника. Стандартный чеккер просто сравнивает вывод участника с ожидаемым выводом.
Когда нужен кастомный чеккер
| Ситуация | Пример |
|---|---|
| Несколько правильных ответов | «Найдите любое решение уравнения» |
| Точность вычислений | «Ответ с точностью до 10^-6» |
| Порядок не важен | «Выведите множество чисел» |
| Частичные баллы | «Чем ближе к оптимуму, тем больше баллов» |
Стандартный чеккер
Если кастомный чеккер не указан, используется стандартный:
- Разбивает вывод на токены (слова)
- Сравнивает токены по порядку
- Игнорирует лишние пробелы и переносы строк
- Результат:
OK(всё совпало) илиWA(не совпало)
Формат кастомного чеккера
Чеккер — это исполняемый файл, который получает:
| Аргумент | Описание |
|---|---|
argv[1] | Путь к файлу с входными данными (input) |
argv[2] | Путь к файлу с выводом участника (output) |
argv[3] | Путь к файлу с ожидаемым ответом (answer) |
Чеккер возвращает:
| Exit code | Значение |
|---|---|
| 0 | Accepted (ответ правильный) |
| 1 | Wrong Answer (ответ неправильный) |
| 2 | Presentation Error (неверный формат) |
Пример чеккера на C++
#include <iostream>
#include <fstream>
#include <cmath>
int main(int argc, char* argv[]) {
std::ifstream input(argv[1]); // входные данные
std::ifstream output(argv[2]); // ответ участника
std::ifstream answer(argv[3]); // ожидаемый ответ
double expected, actual;
answer >> expected;
if (!(output >> actual)) {
std::cerr << "Ошибка чтения ответа участника" << std::endl;
return 1; // WA
}
// Проверка с точностью 10^-6
if (std::abs(expected - actual) < 1e-6) {
std::cout << "OK" << std::endl;
return 0; // AC
} else {
std::cerr << "Expected: " << expected << ", Got: " << actual << std::endl;
return 1; // WA
}
}
Пример чеккера на Python
#!/usr/bin/env python3
import sys
def main():
input_file = sys.argv[1] # входные данные
output_file = sys.argv[2] # ответ участника
answer_file = sys.argv[3] # ожидаемый ответ
with open(answer_file) as f:
expected = float(f.read().strip())
try:
with open(output_file) as f:
actual = float(f.read().strip())
except:
print("Ошибка чтения ответа", file=sys.stderr)
sys.exit(1) # WA
# Проверка с точностью 10^-6
if abs(expected - actual) < 1e-6:
print("OK")
sys.exit(0) # AC
else:
print(f"Expected: {expected}, Got: {actual}", file=sys.stderr)
sys.exit(1) # WA
if __name__ == "__main__":
main()
Чеккер с частичными баллами
Для задач, где нужно начислять частичные баллы:
- Включите опцию «Score from Checker» в настройках
- Чеккер должен выводить score в stdout
- Выберите метод агрегации (SUM, AVG, MIN, MAX)
// Чеккер выводит score от 0 до 100
int main(int argc, char* argv[]) {
// ... проверка ...
double accuracy = calculateAccuracy(expected, actual);
int score = static_cast<int>(accuracy * 100);
std::cout << score << std::endl; // Выводим score
return 0; // Всегда возвращаем 0
}
Загрузка чеккера
- В настройках задачи найдите раздел «Чеккер»
- Загрузите скомпилированный бинарный файл или скрипт
- Убедитесь, что файл исполняемый
Важно: Чеккер должен быть скомпилирован для Linux x64 (Alpine/Debian).
Где взять чеккеры
| Источник | Описание |
|---|---|
| testlib.h | Библиотека от Codeforces для написания чеккеров |
| Polygon | Система подготовки задач с готовыми чеккерами |
| GitHub | Репозитории с примерами чеккеров |
Пример с testlib.h:
#include "testlib.h"
int main(int argc, char* argv[]) {
registerTestlibCmd(argc, argv);
double expected = ans.readDouble();
double actual = ouf.readDouble();
if (std::abs(expected - actual) < 1e-6) {
quitf(_ok, "Correct: %.6f", actual);
} else {
quitf(_wa, "Expected: %.6f, got: %.6f", expected, actual);
}
}
Интерактор (Interactor)
Что такое интерактор
Интерактор — программа для интерактивных задач, где решение участника общается с системой в режиме диалога (вопрос-ответ).
Когда нужен интерактор
| Тип задачи | Пример |
|---|---|
| Угадывание | «Угадай число за минимум вопросов» |
| Игры | «Сыграй оптимально против бота» |
| Запросы | «Узнай структуру графа через запросы» |
Как работает интерактор
┌─────────────┐ stdin ┌──────────────┐
│ Интерактор │ ──────────▶ │ Решение │
│ │ ◀────────── │ участника │
└─────────────┘ stdout └──────────────┘
- Интерактор отправляет данные на stdin решения
- Решение читает stdin и отвечает в stdout
- Интерактор читает stdout решения и отвечает
- Диалог продолжается до завершения
Формат интерактора
Интерактор получает:
| Аргумент | Описание |
|---|---|
argv[1] | Путь к файлу с входными данными (секретные данные) |
argv[2] | Путь к файлу с ответом (для записи результата) |
stdin | Вывод решения участника |
stdout | Ввод для решения участника |
Пример интерактора
Задача: угадать число от 1 до N за минимум вопросов.
#include <iostream>
#include <fstream>
int main(int argc, char* argv[]) {
std::ifstream input(argv[1]); // секретное число
std::ofstream answer(argv[2]); // результат
int secret, n;
input >> n >> secret;
std::cout << n << std::endl; // Сообщаем N участнику
std::cout.flush();
int queries = 0;
std::string command;
int guess;
while (std::cin >> command >> guess) {
queries++;
if (command == "?") { // Вопрос: "число >= guess?"
if (guess <= secret) {
std::cout << "YES" << std::endl;
} else {
std::cout << "NO" << std::endl;
}
std::cout.flush();
} else if (command == "!") { // Ответ
if (guess == secret) {
answer << "OK " << queries << std::endl;
return 0; // AC
} else {
answer << "WA: expected " << secret << ", got " << guess << std::endl;
return 1; // WA
}
}
if (queries > 100) { // Лимит вопросов
answer << "WA: too many queries" << std::endl;
return 1;
}
}
answer << "WA: unexpected end of output" << std::endl;
return 1;
}
Важные моменты
- flush: После каждого вывода делайте
std::cout.flush()илиstd::endl - Лимиты: Ограничивайте количество запросов
- Протокол: Чётко опишите протокол в условии задачи
Загрузка интерактора
- В настройках задачи найдите раздел «Интерактор»
- Загрузите скомпилированный бинарный файл
- Создайте тесты с секретными данными
Режимы оценивания
Стандартный режим
Все тесты пройдены = полные баллы, иначе = 0.
Настройка: По умолчанию, ничего не нужно включать.
Частичные баллы за тесты
Баллы пропорционально пройденным тестам.
Настройка:
- Укажите баллы для каждого теста
- Общий балл = сумма баллов за пройденные тесты
Score from Checker
Баллы определяет чеккер.
Настройка:
- Включите «Score from Checker»
- Выберите метод агрегации:
- SUM: Сумма score по всем тестам
- AVG: Среднее значение
- MIN: Минимальный score
- MAX: Максимальный score (по умолчанию)
- Загрузите чеккер, который выводит score
Best Score Mode
Учитывается лучший результат из всех отправок.
Настройка: Включите «Best Score Mode»
Поведение:
- При Submit сравнивается с текущим сохранённым результатом
- Сохраняется только если новый результат лучше
- Участник не может ухудшить свой результат
Языки программирования
Управление языками
- В настройках задачи перейдите в «Языки»
- Выберите разрешённые языки из списка
- Укажите язык по умолчанию
Доступные языки
| Язык | Версия |
|---|---|
| Python | 3.11 |
| C++ | C++17 |
| Java | 17 |
| JavaScript | Node.js 18 |
| Go | 1.21 |
| Rust | 1.70 |
| C# | .NET 7 |
Шаблон кода по умолчанию
Для каждого языка можно задать шаблон, который будет показан участнику при открытии редактора:
# Python шаблон
def solve():
n = int(input())
# Ваш код здесь
pass
if __name__ == "__main__":
solve()
Ограничение на один язык
Если задача только на одном языке:
- Выберите только один язык
- Отключите «Разрешить выбор языка»
Лимиты отправок
Настройка лимитов
| Параметр | Описание |
|---|---|
| maxSolutionsCount | Максимальное количество отправок |
| solutionsCountPeriod | Период: DAILY (в сутки) или ALL_TIME (за всё время) |
Примеры
| Настройка | Описание |
|---|---|
| 50 / ALL_TIME | 50 отправок на всё соревнование |
| 10 / DAILY | 10 отправок в сутки (сбрасывается в полночь) |
| null | Без ограничений |
Когда использовать
| Ситуация | Рекомендация |
|---|---|
| Обычное соревнование | Без ограничений или 50-100 за всё время |
| Тест на знания | 3-5 попыток за всё время |
| ML/оптимизация | 10-20 в сутки |
Чек-лист создания задачи
- Условие написано понятно
- Формат ввода/вывода описан чётко
- Ограничения указаны (время, память, диапазоны)
- Публичные тесты соответствуют примерам в условии
- Скрытые тесты покрывают граничные случаи
- Ограничения реалистичны для выбранных языков
- Эталонное решение проходит все тесты
- Кастомный чеккер протестирован (если используется)
- Интерактор протестирован (если используется)
Частые вопросы
В: Как добавить задачу с несколькими правильными ответами?
О: Используйте кастомный чеккер, который проверяет, что ответ удовлетворяет условиям, а не сравнивает с эталоном.
В: Как сделать разные лимиты для разных языков?
О: В текущей версии лимиты одинаковые для всех языков. Установите лимит, достаточный для самого медленного языка.
В: Можно ли добавить тесты после публикации?
О: Да, но это может повлиять на уже сданные решения. Рекомендуется добавлять тесты до начала соревнования.
В: Где взять testlib.h?
О: Скачайте с GitHub: https://github.com/MikeMirzayanov/testlib
В: Как протестировать чеккер локально?
О:
./checker input.txt output.txt answer.txt
echo $? # 0 = AC, 1 = WA
В: Почему участники получают TLE на Python, но AC на C++?
О: Python в 10-50 раз медленнее C++. Увеличьте лимит времени или укажите, что задача рассчитана на C++.