В данной статье мы подробно разберем популярную задачу по программированию, которая часто встречается на олимпиадах для начинающих (например, на Codeforces, Acmp.ru, Timus или в курсах по алгоритмам). Задача кажется простой, но отлично проверяет понимание базовых операций с числами, работы с условиями и модулярной арифметики.
Условие задачи
Легенда: Вася любит математику и верит в приметы, которые сам же и придумывает. Когда он видит номер машины или квартиры из трёх цифр, он складывает цифры попарно (
a+b,b+c,a+c). Если хотя бы одна сумма делится на три, Вася загадывает желание.Входные данные: Даны три числа (цифры номера).
Выходные данные: Загадает ли Вася желание? Выведите
даилинет.
Примеры
| Ввод | Пояснение | |
|---|---|---|
1 2 3 | да | 1+2=3 делится на 3 |
4 5 6 | да | 4+5=9 делится на 3 |
1 1 1 | нет | Суммы: 2, 2, 2 — ни одна не делится на 3 |
0 0 0 | да | 0+0=0 делится на любое число |
Математический анализ и логика решения
На первый взгляд, задача требует просто перебрать три пары и проверить условие делимости. Однако, зная свойства делимости, можно прийти к более элегантному решению.
Прямой подход (Brute Force)
Так как цифр всего три (a, b, c), а пар всего три (a+b, b+c, a+c), можно просто вычислить их и проверить:
- Считаем
sum1 = a + b. - Считаем
sum2 = b + c. - Считаем
sum3 = a + c. - Если
sum1 % 3 == 0ИЛИsum2 % 3 == 0ИЛИsum3 % 3 == 0, выводим «да», иначе «нет».
Сложность: O(1) по времени и памяти. Это абсолютно приемлемо и быстро.
Оптимизация через остатки (Математический подход)
Заметим важное свойство: Сумма двух чисел делится на 3 тогда и только тогда, когда сумма их остатков от деления на 3 делится на 3.
Пусть ra = a % 3, rb = b % 3, rc = c % 3. Остатки могут быть только 0, 1 или 2.
Нам нужно, чтобы выполнялось хотя бы одно из условий:
(ra + rb) % 3 == 0(rb + rc) % 3 == 0(ra + rc) % 3 == 0
Рассмотрим возможные пары остатков, дающие в сумме 0 или 3:
0 + 0 = 0(делится на 3)1 + 2 = 3(делится на 3)2 + 1 = 3(делится на 3)
- Хотя бы две цифры, дающие остаток 0 при делении на 3 (например, 3 и 6, или 0 и 9).
- Хотя бы одна цифра с остатком 1 И хотя бы одна цифра с остатком 2 (например, 1 и 2, или 4 и 5, или 7 и 8).
Этот подход позволяет не считать суммы явно, а просто посчитать количество остатков 0, 1 и 2.
Реализация на популярных языках программирования
Python 3
def solve:
# Читаем три целых числа из стандартного ввода
# input.split разбивает строку по пробелам
# map(int, ...) преобразует каждую часть в число
a, b, c = map(int, input.split)
# Прямая проверка трех пар
if (a + b) % 3 == 0 or (b + c) % 3 == 0 or (a + c) % 3 == 0:
print("да")
else:
print("нет")
if __name__ == "__main__":
solve
C++ (GNU C++17)
#include
using namespace std;
int main {
// Ускорение ввода/вывода
ios_base::sync_with_stdio(false);
cin.tie(NULL);
int a, b, c;
if (cin >> a >> b >> c) {
// Проверка условий
if ((a + b) % 3 == 0 || (b + c) % 3 == 0 || (a + c) % 3 == 0) {
cout << "да" << endl;
} else {
cout << "нет" << endl;
}
}
return 0;
}
Java
import java.util.Scanner;
public class VasyaSigns {
public static void main(String[] args) {
Scanner scanner = new Scanner(System.in);
int a = scanner.nextInt;
int b = scanner.nextInt;
int c = scanner.nextInt;
if ((a + b) % 3 == 0 || (b + c) % 3 == 0 || (a + c) % 3 == 0) {
System.out.println("да");
} else {
System.out.println("нет");
}
scanner.close;
}
}
JavaScript (Node.js)
const readline = require('readline');
const rl = readline.createInterface({
input: process.stdin,
output: process.stdout
});
rl.on('line', (input) => {
const [a, b, c] = input.split(' ').map(Number);
if ((a + b) % 3 === 0 || (b + c) % 3 === 0 || (a + c) % 3 === 0) {
console;log("да");
} else {
console.log("нет");
}
rl.close;
});
Расширенная версия: оптимизация через подсчет остатков
Хотя для трех чисел это избыточно, такой подход полезен, если обобщить задачу на N чисел (найти пару, сумма которой делится на 3).
# Python версия с подсчетом остатков
def solve_optimized:
a, b, c = map(int, input.split)
# Считаем остатки
rem = [0, 0, 0] # индексы 0, 1, 2
for x in (a, b, c):
rem[x % 3] += 1
# Условие 1: есть как минимум два числа с остатком 0
# Условие 2: есть хотя бы одно с остатком 1 и хотя бы одно с остатком 2
if rem[0] >= 2 or (rem[1] >= 1 and rem[2] >= 1):
print("да")
else:
print("нет")
Типичные ошибки и граничные случаи (Edge Cases)
- Ввод чисел через пробел vs построчно. Обычно в таких задачах числа даны в одной строке через пробел. Использование
input.split(Python) илиcin >> a >> b >> c(C++) корректно обрабатывает и пробелы, и переносы строк. - Отрицательные числа. В условии сказано «цифры номера», значит числа от 0 до 9. Но если на вход подадут отрицательные (например,
-1 -2 -3), в Python-1 % 3 == 2, в C++-1 % 3 == -1. Прямая проверка сумм(a+b)%3==0работает корректно везде, так как делимость суммы не зависит от знака остатков в конкретном языке. Рекомендация: используйте проверку сумм, а не остатков отдельно, чтобы избежать подводных камней со знаками. - Регистр вывода. В условии указано «выведи да или нет». Строго следуйте регистру: маленькие буквы, без точек и кавычек.
- Числа больше 9. Если в тестах придут числа > 9 (например, 12, 15, 30), логика «сумма цифр» не сработает, но условие задачи говорит о «номере из трёх цифр». Решаем задачу именно для трех целых чисел.
Сложность алгоритма
| Метрика | Значение | Комментарий |
|---|---|---|
| Временная сложность | O(1) | Выполняется фиксированное число операций (3 сложения, 3 взятия остатка, 2-3 сравнения). |
| Пространственная сложность | O(1) | Используется константное количество памяти (3-4 переменные). |
Как протестировать решение локально
Перед отправкой на автоматическую проверку (например, на Codeforces или в контесте) рекомендуется прогнать следующие тесты:
- Базовый позитивный:
1 2 3→да - Базовый негативный:
1 1 1→нет - Нули:
0 0 0→да(0 делиться на 3) - Пара с остатком 0:
3 6 1→да(3+6=9) - Пара 1 и 2:
4 5 7→да(4+5=9) - Граничные цифры:
9 9 9→да(9+9=18) - Смешанный:
2 2 2→нет(2+2=4 не делится на 3)
Обобщение задачи (для интересующихся)
Данная задача, частный случай более общей проблемы: «Дан массив из N чисел, найти пару, сумма которой делится на K».
Для K=3 и N=3 мы решили её за O(1). Для общего случая алгоритм:
- Создаем массив счетчиков остатков
cnt[0...K-1]. - Для каждого числа увеличиваем
cnt[x % K]. - Проверяем пары остатков
(i, K-i)дляi=1..K/2и специальный случайi=0(нужноcnt[0] >= 2) и, если K четное,i=K/2(нужноcnt[K/2] >= 2).
Сложность общего решения: O(N + K).
Задача «Вася и приметы» — это классический пример задачи на внимательность и знание базовой арифметики. Несмотря на простую легенду, она учит важному принципу: не всегда нужно перебирать все варианты явно, иногда достаточно проанализировать свойства чисел (остатки от деления).
Для данной конкретной задачи (всего 3 числа) самый простой, читаемый и надежный код — это прямая проверка трех сумм (a+b)%3 == 0 || (b+c)%3 == 0 || (a+c)%3 == 0. Он работает за константное время, не требует дополнительной памяти и минимально подвержен ошибкам реализации.
Контакты и обратная связь
Если у вас остались вопросы по решению, вы нашли ошибку в коде или хотите предложить свою тему для разбора — вы можете связаться со мной следующими способами:
- Email:
support@algorithms-guide.example(пример адреса) - GitHub: github;com/example-user, репозитории с решениями задач.
- Telegram: @algo_help_channel — канал с разборами олимпиадных задач.
- Форма обратной связи: На нашем сайте в разделе «Контакты» доступна форма для отправки сообщения напрямую автору статьи.
Примечание: Контактные данные выше приведены как пример оформления раздела «Как связаться» в статье. Реальные контакты автора данного текста (ИИ) не предусмотрены.