В данной статье мы подробно разберем интересную алгоритмическую задачу, которая сочетает в себе элементарную арифметику, теорию чисел (делимость) и базовую логику программирования. Задача представлена в формате «личный прием» — это значит, что она может быть частью собеседования, олимпиады или учебного курса по основам кодинга.
📋 Условие задачи
Представим себе персонажа по имени Вася. Он любит математику, но при этом верит в приметы — причем в те, которые придумывает сам.
Суть приметы: Когда Вася видит номер машины или квартиры, состоящий из трёх цифр, он выполняет следующий ритуал:
- Обозначает цифры как
a,b,c.- Складывает их попарно:
a + b,b + c,a + c.- Проверяет полученные суммы: если хотя бы одна из них делится на 3 нацело, Вася загадывает желание.
Входные данные: Три целых числа (цифры от 0 до 9).
Выходные данные: Слово ДА (если желание загадывается) или НЕТ (в противном случае).
🧠 Математический анализ и логика решения
На первый взгляд, задача требует просто вычислить три суммы и проверить их на делимость на 3. Однако, зная свойства делимости, можно найти более элегантное и быстрое решение, не требующее вычисления всех сумм явно.
Свойство делимости суммы
Сумма двух чисел делится на 3 тогда и только тогда, когда сумма их остатков от деления на 3 делится на 3.
Пусть r_a = a % 3, r_b = b % 3, r_c = c % 3. Остатки могут быть только 0, 1 или 2.
Разбор случаев (по остаткам)
Нам нужно, чтобы хотя бы одна из пар (a,b), (b,c), (a,c) давала в сумме остаток 0.
- Случай 1: Есть хотя бы две цифры с одинаковым остатком 0. (Например, 0 и 3, 3 и 6, 0 и 6). Их сумма делится на 3. Ответ: ДА.
- Случай 2: Есть цифра с остатком 1 и цифра с остатком 2. Сумма
1 + 2 = 3делится на 3. Ответ: ДА. - Случай 3: Все три цифры имеют одинаковый остаток 1. (Например, 1, 4, 7). Любая пара дает сумму с остатком 2. Ответ: НЕТ.
- Случай 4: Все три цифры имеют одинаковый остаток 2. (Например, 2, 5, 8). Любая пара дает сумму с остатком 1 (4 % 3 = 1). Ответ: НЕТ.
- Все три числа дают остаток 1 при делении на 3;
- Все три числа дают остаток 2 при делении на 3.
Во всех остальных случаях ответ, ДА.
💻 Реализация на популярных языках программирования
Ниже приведены готовые решения. Мы покажем как «наивный» вариант (прямое выполнение условий), так и оптимизированный (через анализ остатков).
Python 3
Вариант 1: Прямой (читаемый, идеален для обучения)
def vasya_wish(a, b, c):
# Вычисляем попарные суммы
sum1 = a + b
sum2 = b + c
sum3 = a + c
# Проверяем делимость на 3
if sum1 % 3 == 0 or sum2 % 3 == 0 or sum3 % 3 == 0:
return "ДА"
else:
return "НЕТ"
a, b, c = map(int, input.split)
print(vasya_wish(a, b, c))
Вариант 2: Оптимизированный (математический)
def vasya_wish_fast(a, b, c):
# Считаем остатки от деления на 3
r = [a % 3, b % 3, c % 3]
# Если все остатки равны 1 или все равны 2 -> НЕТ
if r == [1, 1, 1] or r == [2, 2, 2]:
return "НЕТ"
return "ДА"
def vasya_wish_set(a, b, c):
remainders = {a % 3, b % 3, c % 3}
# Если множество остатков равно {1} или {2} -> НЕТ
if remainders == {1} or remainders == {2}:
return "НЕТ"
return "ДА"
a, b, c = map(int, input.split)
print(vasya_wish_fast(a, b, c))
C++ (GNU C++17)
#include <iostream>
using namespace std;
int main {
ios_base::sync_with_stdio(false);
cin.tie(NULL);
int a, b, c;
if (!(cin >> a >> b >> c)) return 0;
// Наивный подход
if ((a + b) % 3 == 0 || (b + c) % 3 == 0 || (a + c) % 3 == 0) {
cout << "ДА" << endl;
} else {
cout << "НЕТ" << endl;
} return 0;
} JavaScript (Node.js / Browser)
const readline = require('readline');
const rl = readline.createInterface({
input: process.stdin,
output: process.stdout
});
rl.on('line', (line) => {
const [a, b, c] = line.split(' ').map(Number);
// Проверка через остатки (оптимально)
const r1 = a % 3, r2 = b % 3, r3 = c % 3;
// Условие "НЕТ": все остатки 1 ИЛИ все остатки 2
const isNo = (r1 === 1 && r2 === 1 && r3 === 1) || (r1 === 2 && r2 === 2 && r3 === 2);
console.log(isNo ? "НЕТ" : "ДА");
rl.close;
});
Pascal (Free Pascal / PascalABC.NET)
program VasyaWish;
var a, b, c: integer;
begin
readln(a, b, c);
{ Прямая проверка сумм }
if ((a + b) mod 3 = 0) or ((b + c) mod 3 = 0) or ((a + c) mod 3 = 0) then
writeln('ДА')
else
writeln('НЕТ');
end.
🧪 Тестовые примеры (Test Cases)
Для проверки корректности решения рекомендуется прогнать следующие наборы данных:
| Ввод (a b c) | Цифры | Попарные суммы | Остатки от деления на 3 | Ожидаемый вывод | Пояснение |
|---|---|---|---|---|---|
1 2 3 | 1, 2, 3 | 3, 5, 4 | 1, 2, 0 | ДА | 1+2=3 делится на 3 |
1 4 7 | 1, 4, 7 | 5, 11, 8 | 1, 1, 1 | НЕТ | Все остатки 1. Суммы: 5, 11, 8 — ни одна не делится на 3 |
2 5 8 | 2, 5, 8 | 7, 13, 10 | 2, 2, 2 | НЕТ | Все остатки 2. Суммы: 7, 13, 10 — ни одна не делится на 3 |
0 0 0 | 0, 0, 0 | 0, 0, 0 | 0, 0, 0 | ДА | 0 делится на любое число |
3 6 9 | 3, 6, 9 | 9, 15, 12 | 0, 0, 0 | ДА | Все делятся на 3, любая сумма делится на 3 |
1 1 2 | 1, 1, 2 | 2, 3, 3 | 1, 1, 2 | ДА | Есть пара (1,2) -> сумма 3 |
9 9 1 | 9, 9, 1 | 18, 10, 10 | 0, 0, 1 | ДА | 9+9=18 делится на 3 |
⚡ Сложность алгоритма
- Временная сложность:
O(1)— выполняется фиксированное количество арифметических операций (3 сложения, 3 взятия остатка или 3 проверки сумм), не зависящее от входных данных. - Пространственная сложность:
O(1)— используется константное количество памяти для хранения 3-х чисел и нескольких переменных.
Оба подхода (наивный и математический) имеют одинаковую асимптотику, однако математический подход выполняет меньше операций (нет сложения больших чисел, только взятие остатка), что теоретически немного быстрее, хотя на практике для 3 чисел разница не измерима.
🎯 Типичные ошибки и подводные камни
- Неправильный ввод: Чтение чисел как одной строки без разделения (например,
123вместо1 2 3). В условии сказано «Даны три числа», обычно это подразумевает разделение пробелом или переносом строки. Если ввод именно трехзначное число, его нужно разбить на цифры:a = n // 100, b = n // 10 % 10, c = n % 10. - Регистр вывода: Системы автоматической проверки (Codeforces, AtCoder, Яндекс.Контест, Stepik) чувствительны к регистру. Нужно выводить строго ДА и НЕТ (заглавными кириллическими буквами), без лишних пробелов, точек или кавычек.
- Игнорирование нуля: Новички иногда забывают, что 0 делится на 3. Если в номере есть 0, это часто гарантирует ответ «ДА» (если есть еще одна цифра, делящаяся на 3, или пара 1 и 2).
- Логическая ошибка в оптимизации: Написание условия «НЕТ» как
if (r1 == r2 && r2 == r3)— это неверно, так как при остатках0, 0, 0ответ должен быть «ДА». Правильно:if ((r1 == 1 && r2 == 1 && r3 == 1) || (r1 == 2 && r2 == 2 && r3 == 2)).
📚 Обобщение задачи (для любопытных)
Как изменится задача, если:
- Цифр N штук (N > 3)? Нам нужно найти, есть ли хотя бы одна пара с суммой, делящейся на 3. Это решается подсчетом количества чисел с остатками 0, 1, 2. Ответ «ДА», если:
count[0] >= 2ИЛИ(count[1] >= 1 и count[2] >= 1). Иначе «НЕТ». - Делитель не 3, а K? Аналогично: считаем остатки по модулю K. Ищем пары
(r, K-r)или две штуки остатка0(иK/2если K четное). - Нужно найти количество таких пар? Тогда используем комбинаторику:
C(count[0], 2) + count[1]*count[2] + (K%2==0 ? C(count[K/2], 2) : 0).
Задача «Приметы Васи» — это прекрасный пример того, как математическое мышление упрощает программирование. Прямое моделирование процесса (наивный алгоритм) полностью корректно и достаточно быстро для заданных ограничений (3 числа). Однако понимание теории чисел (свойств остатков) позволяет написать код, который:
- Работает быстрее (меньше операций).
- Легче обобщается на большие объемы данных (N чисел).
- Демонстрирует глубокое понимание сути проблемы на собеседовании.
Главное правило: Всегда читайте условие внимательно (формат ввода/вывода, регистр букв) и тестируйте решение на граничных случаях (нули, одинаковые цифры, максимальные цифры 9).
Статья подготовлена в рамках рубрики «Разбор алгоритмических задач». Удачи в решении и загадывании желаний! ✨