Число способов разменять сумму Н

я столкнулся с этой проблемой:

http://www.geeksforgeeks.org/dynamic-programming-set-7-coin-change/

задано значение N, если мы хотим сделать изменение для N центов, и у нас есть бесконечный запас каждого из S = { S1, S2,.. , SM} ценные монеты, сколько способов мы можем внести изменения? Порядок монет не имеет значения.

например, для N = 4 и S = {1,2,3}, существует четыре решения: {1,1,1,1},{1,1,2},{2,2},{1,3}. Так выход должен быть 4. Для N = 10 и S = {2, 5, 3, 6}, существует пять решений: {2,2,2,2,2}, {2,2,3,3}, {2,2,6}, {2,3,5} и {5,5}. Таким образом, выход должен быть 5.

Я придумал решение:

// recurrence relation
count[N] = count[N-d] for all denomination <= N

Source code
-----------

public static int numWays(int N, int[] denoms) {
  if (N == 0)
     return 0;

  int[] ways = new int[N+1];
  ways[0] = 1;

  for (int i=1; i<=N; i++) {
     ways[i] = 0;
     for (int d : denoms) {
        if (d <= i) {
           ways[i] += ways[i-d];
        }
     }
  }

  return ways[N];
}

но это дубликаты, которые имеют те же номиналы, но в другом порядке. Например, если номиналы = {1,2} и N=3, то это считается {1,1,1}, {2,1}, {1,2} который имеет повторяющуюся запись {1,2}.

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

1 ответов


пусть C(i, j) количество способов сделать общей сумме i использование монет номиналов S1, ..., Sj. Ваш код реализует следующее повторение (упорядоченные способы).

C(i, m) | i <  0 = 0
        | i == 0 = 1
        | i >  0 = sum_{j = 1}^m C(i - Sj, m)

связанный код реализует другое повторение (неупорядоченные способы).

C(i, j) | i <  0           = 0
        | i == 0           = 1
        | i >  0 && j <= 0 = 0
        | i >  0 && j >  0 = C(i - Sj, j) + C(i, j - 1)

разница между двумя кодами тонкая: более или менее точно, как вложены циклы. Ваш добавляет все условия для i до i + 1, но связанный код добавляет j термин для каждого i, потом j + 1 срок для каждого i, etc. В результате, когда связанный код, рассматривает возможность использования деноминации-Sj монета итого i, он неявно рассматривает только те решения, которые продолжаются с монетами номиналов S1, ..., Sj, потому что текущая сумма для i - Sj не включает в себя возможности, которые используют другие монеты.