Задача из соревнования по программированию ... Digit Sums - PullRequest
3 голосов
/ 01 сентября 2010

Мне нужна помощь в решении задачи N из этого более раннего конкурса :

Задача N: Цифры Суммы

Учитывая 3 натуральных числаA, B и C, найдите, сколько положительных целых чисел, меньших или равных A, если они выражены в базе B, имеют цифры, которые суммируются с C.

Входные данные состоят из серии строк, каждая из которых содержит три целых числа, A, B и C, 2 ≤ B ≤ 100, 1 ≤ A, C ≤ 1 000 000 000.Числа A, B и C даны в базе 10 и разделены одним или несколькими пробелами.Ввод завершается строкой, содержащей три нуля.

Выходом будет число чисел для каждой входной строки (оно должно быть задано в базе 10).

Пример ввода

100 10 9
100 10 1
750000 2 2
1000000000 10 40
100000000 100 200
0 0 0

Пример ввода

10
3
189
45433800
666303

Соответствующие правила:

  1. Считать все вводимые данные с клавиатуры, т. Е. Использовать stdin, System.in, cin или эквивалентный.Ввод будет перенаправлен из файла, чтобы сформировать вход для вашего представления.

  2. Записать весь вывод на экран, т. Е. Использовать stdout, System.out, cout или эквивалентный.Не пишите stderr.НЕ ИСПОЛЬЗУЙТЕ и даже не включайте какие-либо модули, которые позволяют напрямую манипулировать экраном, такие как conio, Crt или что-либо подобное.Вывод из вашей программы перенаправляется в файл для последующей проверки.Использование прямого ввода / вывода означает, что такой вывод не перенаправлен и, следовательно, не может быть проверен.Это может означать, что правильная программа отклонена!

  3. Если не указано иное, все целые числа на входе будут вписываться в стандартное 32-разрядное компьютерное слово.Смежные целые числа в строке будут разделены одним или несколькими пробелами.

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

Заранее спасибо, Джон.

Ответы [ 4 ]

6 голосов
/ 01 сентября 2010

Другие люди указали на тривиальное решение: переберите все числа от 1 до A. Но эта проблема, на самом деле, может быть решена в почти постоянное время: O(length of A), что составляет O(log(A)).

  1. Код предоставлен для базы 10. Адаптировать его для произвольной базы тривиально.
  2. Чтобы достичь вышеупомянутой оценки времени, вам нужно добавить запоминание к рекурсии. Дайте мне знать, если у вас есть вопросы по этой части.

Теперь рекурсивная функция. Написано на Java, но все должно работать на C # / C ++ без каких-либо изменений. Он большой, но в основном из-за комментариев, в которых я пытаюсь уточнить алгоритм.

// returns amount of numbers strictly less than 'num' with sum of digits 'sum'
// pay attention to word 'strictly'
int count(int num, int sum) {
    // no numbers with negative sum of digits
    if (sum < 0) {
        return 0;
    }

    int result = 0;

    // imagine, 'num' == 1234
    // let's check numbers 1233, 1232, 1231, 1230 manually
    while (num % 10 > 0) {
        --num;

        // check if current number is good
        if (sumOfDigits(num) == sum) {
            // one more result
            ++result;
        }
    }

    if (num == 0) {
        // zero reached, no more numbers to check
        return result;
    }

    num /= 10;

    // Using example above (1234), now we're left with numbers
    // strictly less than 1230 to check (1..1229)
    // It means, any number less than 123 with arbitrary digit appended to the right
    // E.g., if this digit in the right (last digit) is 3,
    // then sum of the other digits must be "sum - 3"
    // and we need to add to result 'count(123, sum - 3)'

    // let's iterate over all possible values of last digit
    for (int digit = 0; digit < 10; ++digit) {
        result += count(num, sum - digit);
    }

    return result;
}

Вспомогательная функция

// returns sum of digits, plain and simple
int sumOfDigits(int x) {
    int result = 0;
    while (x > 0) {
        result += x % 10;
        x /= 10;
    }
    return result;
}

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

    int A = 12345;
    int C = 13;

    // recursive solution
    System.out.println(count(A + 1, C));

    // brute-force solution 
    int total = 0;
    for (int i = 1; i <= A; ++i) {
        if (sumOfDigits(i) == C) {
            ++total;
        }
    }
    System.out.println(total);

Вы можете написать более полный тестер, проверяющий все значения A, но общее решение кажется правильным. (Я попробовал несколько случайных А и С.)

Не забывайте, вы не можете протестировать решение для A == 1000000000 без запоминания: оно будет работать слишком долго. Но с запоминанием вы можете проверить это даже для A == 10^1000.

редактировать
Просто чтобы доказать концепцию, запоминание бедного человека. (в Java, в других языках хеш-таблицы объявляются по-разному) Но если вы хотите чему-то научиться, лучше попробовать сделать это самостоятельно.

// hold values here
private Map<String, Integer> mem;

int count(int num, int sum) {
    // no numbers with negative sum of digits
    if (sum < 0) {
        return 0;
    }

    String key = num + " " + sum;
    if (mem.containsKey(key)) {
        return mem.get(key);
    }

    // ... 
    // continue as above...
    // ...

    mem.put(key, result);
    return result;
}
2 голосов
/ 01 сентября 2010

Вот то же запомненное рекурсивное решение, которое опубликовал Рыбак, но с более простой реализацией, по моему скромному мнению:

HashMap<String, Integer> cache = new HashMap<String, Integer>();

int count(int bound, int base, int sum) {
    // No negative digit sums.
    if (sum < 0)
        return 0;

    // Handle one digit case.
    if (bound < base)
        return (sum <= bound) ? 1 : 0;

    String key = bound + " " + sum;
    if (cache.containsKey(key))
        return cache.get(key);

    int count = 0;
    for (int digit = 0; digit < base; digit++)
        count += count((bound - digit) / base, base, sum - digit);

    cache.put(key, count);
    return count;
}
1 голос
/ 01 сентября 2010

Это не полное решение (без разбора ввода).Чтобы получить число в базе B, несколько раз возьмите по модулю B, а затем делите на B, пока результат не станет 0. Это эффективно вычисляет цифру base-B справа, а затем сдвигает число вправо.

int A,B,C; // from input
for (int x=1; x<A; x++)
{
    int sumDigits = 0;
    int v = x;
    while (v!=0) {
       sumDigits += (v % B);
       v /= B;
    }
    if (sumDigits==C)
       cout << x;
}

Это подход грубой силы.Может быть возможно вычислить это быстрее, определяя, какие наборы базовых цифр B складываются в C, упорядочивая их во всех перестановках, которые меньше A, и затем работая в обратном направлении от этого, чтобы создать исходное число.

0 голосов
/ 01 сентября 2010

Yum.

Попробуйте это:

int number, digitSum, resultCounter = 0;

for(int i=1; i<=A, i++)
{
   number = i; //to avoid screwing up our counter
   digitSum = 0;
   while(number > 1)
   {
      //this is the next "digit" of the number as it would be in base B; 
      //works with any base including 10.
      digitSum += (number % B);
      //remove this digit from the number, square the base, rinse, repeat 
      number /= B;
   }
   digitSum += number;

   //Does the sum match?       
   if(digitSum == C)
      resultCounter++;
}

Это ваш основной алгоритм для одной строки. Теперь вы заключаете это в другой цикл For для каждой полученной строки ввода, которой предшествует сама фаза сбора ввода. Этот процесс может быть упрощен, но мне не хочется кодировать весь ваш ответ, чтобы увидеть, работает ли мой алгоритм, и это выглядит правильно, тогда как более простые приемы сложнее пройти при проверке.

Способ, которым это работает, делится по модулю на силы основания. Простой пример, 1234 в базе 10:

1234 % 10 = 4
1234 / 10 = 123 //integer division truncates any fraction
123 % 10 = 3 //sum is 7
123 / 10 = 12
12 % 10 = 2 //sum is 9
12 / 10 = 1 //end condition, add this and the sum is 10

Более сложный пример, который можно выяснить при осмотре, - это то же число в базе 12:

1234 % 12 = 10 //you can call it "A" like in hex, but we need a sum anyway
1234 / 12 = 102
102 % 12 = 6 // sum 16
102/12 = 8
8 % 12 = 8 //sum 24
8 / 12 = 0 //end condition, sum still 24.

Таким образом, 1234 в базе 12 будет написано 86A. Проверьте математику:

8*12^2 + 6*12 + 10 = 1152 + 72 + 10 = 1234

Получайте удовольствие, оборачивая остальную часть кода вокруг этого.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...