Задав число N, найдите количество способов записать его как сумму двух или более последовательных целых чисел. - PullRequest
5 голосов
/ 27 декабря 2010

Вот проблема , помеченная как динамическое программирование (Учитывая число N, найдите количество способов записать его как сумму двух или более последовательных целых чисел) ипример 15 = 7 + 8, 1 + 2 + 3 + 4 + 5, 4 + 5 + 6

Я решил с математикой так:

a + (a + 1) + (a + 2) + (a + 3) + ... + (a + k) = N

(k + 1) * a + (1 + 2 + 3 + ... +k) = N

(k + 1) a + k (k + 1) / 2 = N

(k+ 1) * (2 * a + k) / 2 = N

Затем проверьте, что если N делится на (k + 1) и (2 * a + k), то я могунайти ответ за O (sqrt (N)) время

Вот мой вопрос, как вы можете решить эту проблему с помощью динамического программирования ?а в чем сложность (о)?

PS: извините, если это дублирующий вопрос.Я искал, но я могу найти

Ответы [ 5 ]

1 голос
/ 01 июля 2018

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

private static int count(int n) {
    int i = 1, j = 1, count = 0, sum = 1; 
    while (j<n) {
        if (sum == n) { // matched, move sub-array section forward by 1
            count++; 
            sum -= i; 
            i++;
            j++;
            sum +=j; 
        } else if (sum < n) { // not matched yet, extend sub-array at end
            j++; 
            sum += j; 
        } else { // exceeded, reduce sub-array at start
            sum -= i; 
            i++; 
        }
    }
    return count;
}
1 голос
/ 27 декабря 2010

Мы можем использовать динамическое программирование для вычисления сумм от 1 + 2 + 3 + ... + K для всех K до N. sum[i] ниже представляет сумму 1 + 2 + 3 + ... + i.

sum = [0]
for i in 1..N:
  append sum[i-1] + i to sum

С помощью этих сумм мы можем быстро найти все последовательности последовательных целых чисел, суммирующих с N. Сумма i + (i + 1) + (i + 2) + ... j равна sum[j] - sum[i] + 1. Если сумма меньше N, мы увеличиваем j. Если сумма больше N, мы увеличиваем i. Если сумма равна N, мы увеличиваем наш счетчик и оба i и j.

i = 0
j = 0
count = 0
while j <= N:
  cur_sum = sum[j] - sum[i] + 1
  if cur_sum == N:
    count++
  if cur_sum <= N:
    j++
  if cur_sum >= N:
    i++

Хотя есть и лучшие альтернативы, чем использование этого динамического программного решения. Массив sum может быть вычислен математически по формуле k (k + 1) / 2, поэтому мы можем вычислять его на лету без необходимости в дополнительном хранилище. Более того, поскольку мы только смещаем конечные точки суммы, с которой работаем, максимум на 1 в каждой итерации, мы можем вычислить ее еще более эффективно на лету, добавляя / вычитая добавленные / удаленные значения. 1014 *

i = 0
j = 0
sum = 0
count = 0
while j <= N:
  cur_sum = sum[j] - sum[i] + 1
  if cur_sum == N:
    count++
  if cur_sum <= N:
    j++
    sum += j
  if cur_sum >= N:
    sum -= i
    i++
1 голос
/ 27 декабря 2010

Для нечетных N эта задача эквивалентна нахождению числа делителей N, не превышающего sqrt(N). (Для четных N есть пара поворотов.) Эта задача занимает O(sqrt(N)/ln(N)), если у вас есть доступ к списку простых чисел, O(sqrt(N)) в противном случае.

Я не понимаю, как здесь может помочь динамическое программирование.

0 голосов
/ 17 сентября 2013

1) Для n> = 0 целое число, сумма целых чисел от 0 до n равна n * (n + 1) / 2.Это классика: напишите эту сумму сначала так: S = 0 + 1 + ... + n, а затем вот так: S = n + (n-1) + ... + 0 Вы видите, что 2 * S равнов (0 + n) + (1 + n-1)) + ... + (n + 0) = (n + 1) n, так что S = n (n + 1) /2 действительно.(Хорошо известно, но предпочтительнее, чтобы мой ответ был самодостаточным).

2) Начиная с 1, если мы отмечаем минусы (m, n), сумма m + (m + 1) +... (n-1) + n последовательная сумма целых чисел между положительными (то есть> = 0) такая, что 1 <= m <= n, мы видим, что: cons (m, n) = (0 + 1 + ... + n) - (0 + 1 + ... + (m-1)), что дает 1: cons (m, n) = n * (n + 1) / - m (m-1) / 2 </p>

3) Затем вопрос сводится к следующему: во сколько раз мы можем записать N в форме N = cons (m, n) с m, n целыми числами, такими, что 1 <= m <= n?Если у нас N = cons (m, n), это эквивалентно m ^ 2 - m + (2N -n ^ 2 -n) = 0, то есть вещественному многочлену T ^ 2 - m + (2N -n^ 2 -n) имеет реальный корень m: его дискриминантная дельта должна быть квадратом.Но у нас есть: </p>

delta = 1 - 3*(2*N - n^2 - n)

И эта дельта является целым числом, которое должно быть квадратом.Следовательно, существует целое число M такое, что:

delta = 1 - 3*(2*N - n^2 - n) = M^2

, то есть

M^2 = 1 - 6*N + n(n+1)

n (n + 1), всегда делится на 2 (например, в 2 раза больше нашего S отначало, но здесь есть более тривиальная причина, среди последовательных целых чисел нужно быть четным), и поэтому M ^ 2 нечетно, подразумевая, что M должно быть нечетным.

4) Переписатьили предыдущее уравнение как:

n^2 + n + (1-6*N - M^2) = 0

Это показывает, что действительный полином X ^ 2 + X + (1-6 * N - M ^ 2) имеет действительный ноль, n: следовательно, его дискриминантная гамма должна бытьквадрат, но:

gamma = 1 - 4*(1-6*N-M^2)

и это должен быть квадрат, так что здесь снова существует целое число G такое, что

G^2 = 1 - 4*(1-6*N-M^2)

G^2 = 1 + 4*(2*N + m*(m-1))

, которое показывает, что, поскольку M нечетно, G тоже нечетно.

5) Подстановка M ^ 2 = 1 - 4 * (2 * N - n * (n + 1)) в G ^ 2 = 1 + 4* (2 * N + m * (m-1))) дает:

G^2 - M^2 = 4*(2*N + m*(m-1)) + 4*(2*N -n*(n+1))
          = 16*N + 4*( m*(m-1) - n*(n+1) )
          = 16*N - 8*N (because N = cons(m,n))
          = 8*N

И, наконец, это можно переписать как:

(G-M)*(G+M) = 8*N, that is

[(G-M)/2]*[(G+M)/2] = 2*N

где (GM) / 2и (G + M) / 2 являются целыми числами (GM и G + M четные, поскольку G и M нечетные)

6) Таким образом, при каждом способе записи N как cons (m, n) мы можем связать путь (и только один путь, так как M и G определены однозначно) с множителем 2 * N в произведение x * y с x =(GM) / 2 и y = (G + M) / 2, где G и M - два нечетных целых числа.Так как G = x + y и M = -x + y, поскольку G и M нечетны, мы видим, что x и y должны иметь противоположные четности.Таким образом, среди х и у одно четное, а другое нечетное.Таким образом, 2 * N = x * y, где среди x и y одно четное, а другое нечетное.Пусть c будет нечетным среди x и y, а d будет четным.Тогда 2 * N = c * d, следовательно, N = c * (d / 2).Таким образом, c является нечетным числом, делящим N, и однозначно определяется N, как только N = cons (m, n).Взаимно, как только N имеет нечетный делитель, можно перепроектировать весь этот материал, чтобы найти n и m.

7) * Вывод: существует один к одномусоответствие между числом способов записи N = cons (m, n) (которое, как мы видели, является числом способов записи N в виде суммы последовательных целых чисел) и числом нечетных делителей числа N. *

8) Наконец, число, которое мы ищем, является числом нечетных делителей n. Полагаю, что решить эту проблему с помощью DP или любого другого проще, чем решить предыдущую.

0 голосов
/ 27 декабря 2010

Чтобы решить эту проблему, мы попробуем все суммы последовательных целых чисел в [1, M], где M получается из M (M + 1) / 2 = N.

<code>int count = 0
for i in [1,M]
  for j in [i, M]
    s = sum(i, j) // s = i + (i+1) + ... + (j-1) + j
    if s == N 
       count++
    if s >= N
       break
return count

Поскольку мы не хотим вычислять sum(i, j) в каждой итерации с нуля, мы будем использовать технику, известную как «запоминание». Давайте создадим матрицу целых чисел sum[M+1][M+1] и зададим sum[i][j] для i + (i + 1) + ... + (j-1) + j.

<code>for i in [1, M]
  sum[i][i] = i</p>

<p>int count = 0
for i in [1, M]
  for j in [i + 1, M]
    sum[i][j] = sum[i][j-1] + j
    if sum[i][j] == N
      count++
    if sum[i][j] >= N
      break
return count

Сложность, очевидно, O (M ^ 2), то есть O (N)

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