Максимальная сумма цифр из пар, сумма которых составляет число

Рейтинг: 1Ответов: 2Опубликовано: 09.03.2023

Коллеги, прошу помощи.

Дано число, пусть будет 45 (число может быть очень большим) нужно разбить данное число на пары чисел, так что бы сумма всех цифр в полученных парах была максимальной из всех возможных и вывести этот максимум. Так, для числа 45 первая пара выдающая максимальную сумму: 6, 39 (6 + 3 + 9) = 18, для числа 29 - (1, 28) == 11, для числа 1140 - (141, 999) - 33, для 7019 - (20, 6999) - 35... при этом, нужно еще и уложиться в ограничение по времени в 1200ms...

я пробовал:

def solve(n): 
    def func(n: int) -> int:
        return sum(list(map(int, list(str(n)))))
    stack = 0
    for i in range(1, n):
        summ = func(i) + func(n-i)
        if summ > stack:
            stack = summ

    return stack

но на больших числах мое решение не проходит по времени

Подозреваю, что нужно как то прерывать цикл как только найдена максимальная сумма, что бы не выполнялось дальше, но как его прервать? Пробовал так:

i = 1
stack = 0
while i < n:
    summ = func(i) + func(n - i)
    stack = summ
    print(stack)
    i += 1
    # if summ > stack:
    #     print(summ, stack)
    #     break
    while stack > summ:
        continue
    else:
        print(summ)
        break

Но в таком варианте не выдает максимальную сумму...

Найти каких либо закономерностей в числах я не смог...

Помогите, коллеги, наведите неофита на пусть истинный...

Ответы

▲ 4Принят

UPD

Задача спрашивает, на самом деле, о максимальной сумме. Тут даже раскладывать на пары не надо:

def digits(n:int) -> list[int]:
    if n == 0:
        return [0]
    res = []
    while n > 0:
        res.append(n%10)
        n //=10
    return res

def solve(n:int) -> int:
    ndigs = digits(n)
    s, ndigs = sum(ndigs), ndigs[:-1]
    while len(ndigs) > 0 and ndigs[0] == 9:
        ndigs = ndigs[1:]
    return s + 9*len(ndigs)

Обоснование ниже в тексте


Смотрите, как можно разложить число 45:

  • начинаем справа. Берём 5. Эту цифру можно получить из суммы маленьких чисел, например 2+3, а можно из суммы цифр побольше, например 7+8. В первом случае сумма цифр будет 5, а во втором 15.
  • следующая цифра 4.
    • Если мы раскладывали 5 на сумму малых, то переноса нет, и 4 надо разложить на сумму малых (так как это крайняя цифра) - 4 = 2+2. Получилось разложение 45 = 22+23, сумма цифр 9
    • но если 5 разложили на сумму больших, то был перенос десятки в следующий разряд, и надо раскладывать цифру 3. Возмём, к примеру, 1+2. Получится 45 = 17+28, сумма цифр 18.

Идея понятна? Нужно начинать раскладывать справа, и каждый раз стараться разложить на сумму больших цифр, которые дадут перенос в следующий разряд. Так можно разложить все цифры, кроме 9. Девятка раскладывается только на сумму малых цифр (причём, что характерно, не важно каких именно - сумма всё равно будет 9).

То есть максимальная сумма цифр для разложения будет сумма цифр исходного числа плюс число возможных переносов, помноженное на 9. Например, 29 - сумма цифр 11. Переносов нет - первая цифра 9, вторая цифра 2 - конечная. Поэтому как ни раскладывай, в сумме будет 11 (11+18, 12+17, 13+16, 14+15). В числе 45 один возможный перенос - это первая цифра 5. Вторая цифра крайняя, переноса не будет. Поэтому максимальная сумма в разложении (4+5)+9 = 18. В числе 7019 нет переноса для единиц, там стоит 9, зато есть переносы для десятков и сотен, поэтому максимальная возможная сумма цифр (7+1+9)+2*9 = 35.

Кстати, переносы невозможны только для нескольких девяток подряд в младших разрядах, поэтому максимальную сумму можно посчитать без разложения:

  • просуммировать цифры исходного числа (7019 -> 17)
  • отбросить справа все девятки, идущие подряд (7019 -> 701)
  • отбросить цифру слева (701->01)
  • количество оставшихся цифр умножить на 9 и прибавить сумму цифр исходного числа (2*9 + 17 = 35)

Разложить можно разными способами. Я сделал разложение примерно пополам.

def digits(n:int):
    "Генератор цифр числа, начиная с единиц"
    while n > 0:
        yield n%10
        n = n // 10

def num(digits:list[int]) -> int:
    "Строит число из списка цифр, начинающийся с единиц."
    res = 0
    for d in reversed(digits):
        res = res*10+d
    return res

def split(n:int) -> tuple[list[int],list[int]]:
    "Возвращает два списка цифр, которые представляют числа `a` и `b` такие, что `a+b=n`"
    n_digits = list(digits(n))
    a, b, carry =[], [], 0
    for d in n_digits[:-1]:
        d = (d - carry + 10)%10
        if d == 9:
            a.append(5)
            b.append(4)
        else:
            h1 = (10+d)//2
            h2 = (10+d)-h1
            a.append(h1)
            b.append(h2)
            carry = 1

    d = n_digits[-1]
    d = (d - carry + 10)%10
    h1 = d//2
    h2 = d-h1
    a.append(h1)
    b.append(h2)
    return a,b

def solve(n:int) -> tuple[int, int, int]:
    """
    Возвращает числа (а, b, s) такие что a+b=n, s - сумма цифр a и b.
    """
    a,b = split(n)
    return num(a), num(b), sum(a)+sum(b)

Проверка

def test(n):
    a,b,s = solve(n)
    print(f"{n} == {a} + {b} ({a+b}), сумма цифр {s}")

test(29)
test(45)
test(1140)
test(6699)
test(7019)
29 == 15 + 14 (29), сумма цифр 11
45 == 17 + 28 (45), сумма цифр 18
1140 == 565 + 575 (1140), сумма цифр 33
6699 == 2855 + 3844 (6699), сумма цифр 39
7019 == 3555 + 3464 (7019), сумма цифр 35
▲ 3

Если посмотреть результат работы для первых нескольких сотен чисел, то можно заметить что одно из чисел имеет вид {k}{999...999}. Поэтому я сначало нахожу порядок числа (n), затем просто перебираю различные первые цифры и выбираю максимальное их подходящих

У меня получилось как то так:

def f(x):
    if x < 10:
        return x
    n = len(str(x)) - 1
    for i in range(10):
        a = (i + 1) * 10 ** n - 1
        if a > x:
            i -= 1
            break
    a = (i + 1) * 10 ** n - 1
    b = x - a
    return sum(map(int, str(a)+str(b)))

От цикла for можно попробовать избавиться, но мне лень. Проверил на codewars, все тесты проходит, поэтому в оптимизации не вижу смысла.

Я все таки избавился от цикла и код стал гораздо красивее:

def f(x):
    n = 10 ** (len(str(x)) - 1)
    k = (x + 1) // n
    
    a = k*n-1
    b = x - a
    return sum(map(int, str(a)+str(b)))