С Новым годом! Форум программистов, компьютерный форум, киберфорум
Алгоритмы
Войти
Регистрация
Восстановить пароль
Блоги Сообщество Поиск Заказать работу  
 
Рейтинг 4.71/55: Рейтинг темы: голосов - 55, средняя оценка - 4.71
0 / 0 / 0
Регистрация: 06.09.2018
Сообщений: 8

Разбиение числа на неповторяющиеся(различные) слагаемые

18.12.2019, 18:46. Показов 11441. Ответов 6

Студворк — интернет-сервис помощи студентам
Со стандартного устройства ввода вводится в первой строке число N – разбиваемое
число. 1<=N<=1000.

Нужно выдать на стандартное устройство вывода через пробел N чисел. K-тое число
должно показывать, сколько существует разбиений числа N на слагаемые без повторов, в
которых наибольшее слагаемое не превосходит K.

Например, для N=10:

0 0 0 1 3 5 7 8 9 10

Я написал решение через рекурсию с запоминанием, но не проходит по времени, хотелось бы найти решение

C++
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
#include <iostream>
 
using namespace std;
long long Sl(long long** d, int n, int k)
//реккурентно заданная формула для нахождения количества(через рекурсию)
{
    if (n < 0 || k < 0)
        return 0;
 
    if (d[n][k] > 0 && n >= 0 && k >= 0)
        return d[n][k];
 
    if (k > n&& d[n][n] > 0)
        return d[n][n];
 
    if (k > n&& d[n][n] < 0)
        return Sl(d, n, n);
 
    if (k == 0 && n > 0)
        return 0;
    if (n - k > -1 && k - 1 > -1)
    {
        if (d[n][k - 1] > 0)
        {
            if (d[n - k][k - 1] > 0)
            {
                d[n][k] = d[n][k - 1] + d[n - k][k - 1];
            }
            else
            {
                d[n][k] = d[n][k - 1] + Sl(d, n - k, k - 1);
            }
        }
    }
    d[n][k]= Sl(d,n, k - 1) + Sl(d,n - k, k - 1);
    return d[n][k];
}
int main()
{
    int n;
    cin >> n;
    long long** d= new long long* [n+1];
    //таблица для уже найденных количеств слагаемых
    for (int i = 0; i < n+1; i++)
    {
        d[i] = new long long[n+1];
        for (int j = 0; j < n+1; j++)
        {
            d[i][j] = -1;
        }
    }
    if (n > 1)
    {
        d[0][0] = 1;
        d[1][1] = 1;
    }
    for (int i = 1; i <= n; i++)
    {
        cout << Sl(d,n, i) << (i == n ? "" : " ");
    }
}
0
Лучшие ответы (1)
Programming
Эксперт
39485 / 9562 / 3019
Регистрация: 12.04.2006
Сообщений: 41,671
Блог
18.12.2019, 18:46
Ответы с готовыми решениями:

Разложение числа на неповторяющиеся слагаемые
Собственно, задача сказана. Вот код для количества:#include &lt;iostream&gt; #include &lt;stack&gt; #include &lt;utility&gt; using namespace std;...

Разбиение числа на слагаемые
разбиваю число на слагаемые рекурсивно, все с виду вроде в шоколаде, но если внюхаться, то нет: вот вывод моей функции: 4+1 3+2 ...

Разбиение числа на слагаемые
Всем привет! Нашёл много примеров на данную тему, но то, что мне нужно - не нашёл. Допустим, на вход даётся число 10 и говорится,...

6
Модератор
Эксперт функциональных языков программирования
3133 / 2280 / 469
Регистрация: 26.03.2015
Сообщений: 8,876
19.12.2019, 12:42
Алгоритм (без оптимизации по производительности):
F#
1
2
3
4
let solve n =
    let addWithShift xx k =
        List.append (List.replicate k 0) (List.take (n + 1 - k) xx) |> List.map2 (+) xx
    [1..n] |> List.scan addWithShift (1 :: List.replicate n 0) |> List.map List.last |> List.skip 1
Пусть список (состояние) содержит количество разбиений чисел от 0 до N на слагаемые без повторов, в которых наибольшее слагаемое не превосходит K. Чтобы получить состояние для k из состояния для k-1, нужно прибавить к нему сдвинутую на k копию. Для k = 0 список выглядит [1, 0, 0, 0, ....] (всего N+1 элементов). Выводить нужно последний элемент (количество разбиений числа N). Для k = 0 выводить не надо.

Добавлено через 2 минуты
В С++ логично для хранения состояния использовать массив, и менять его нужно справа налево (с конца).
0
0 / 0 / 0
Регистрация: 06.09.2018
Сообщений: 8
21.12.2019, 14:51  [ТС]
Цитата Сообщение от Shamil1 Посмотреть сообщение
Алгоритм (без оптимизации по производительности):
F#
1
2
3
4
let solve n =
    let addWithShift xx k =
        List.append (List.replicate k 0) (List.take (n + 1 - k) xx) |> List.map2 (+) xx
    [1..n] |> List.scan addWithShift (1 :: List.replicate n 0) |> List.map List.last |> List.skip 1
Пусть список (состояние) содержит количество разбиений чисел от 0 до N на слагаемые без повторов, в которых наибольшее слагаемое не превосходит K. Чтобы получить состояние для k из состояния для k-1, нужно прибавить к нему сдвинутую на k копию. Для k = 0 список выглядит [1, 0, 0, 0, ....] (всего N+1 элементов). Выводить нужно последний элемент (количество разбиений числа N). Для k = 0 выводить не надо.

Добавлено через 2 минуты
В С++ логично для хранения состояния использовать массив, и менять его нужно справа налево (с конца).
Не совсем понятно, как именно получать количество, а именно, что за копии сдвинутые на k ?
0
Модератор
Эксперт функциональных языков программирования
3133 / 2280 / 469
Регистрация: 26.03.2015
Сообщений: 8,876
22.12.2019, 10:46
Копия, сдвинутая на 2:
Code
1
2
1 1 2 3 1 0 0 0
0 0 1 1 2 3 1 0
Список (состояние) содержит количество разбиений чисел от 0 до N. Соответственно, последний элемент содержит количество разбиений числа N.
0
0 / 0 / 0
Регистрация: 06.09.2018
Сообщений: 8
24.12.2019, 18:21  [ТС]
Shamil1 Спасибо большое за ваши рассуждения,но я так и не понял идеи вашего решения, но нашёл своё, может кому понадобится:

C++
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
#include <iostream>
#include <iomanip>
using namespace std;
 
int main()
{
    int n;
    cin >> n;
    long long** d = new long long* [n + 1];
    //таблица для уже найденных количеств слагаемых
    for (int i = 0; i < n + 1; i++)
    {
        d[i] = new long long[n + 1];
        for (int j = 0; j < n + 1; j++)
        {
            d[i][j] = 0;
        }
    }
    d[0][0] = 1;
    d[1][1] = 1;
    for (int i = 0; i < n + 1; i++)
    {
        for (int j = 0; j <= i; j++)
        {
            if (j == 0 && i != 0)
            {
                d[i][j] = 0;
            }
            if (i > 0 && j > 0)
                d[i][j] = (d[i - j][j - 1] + d[i][j - 1])%1000000000;
 
        }
        for (int k = i + 1; k < n + 1; k++)
        {
            d[i][k] = d[i][i];
        }
    }
    for (int i = 1; i < n + 1; i++)
    {
        cout << (d[n][i])%1000000000 << (i == n ? "" : " ");
    }
}
0
Модератор
Эксперт функциональных языков программирования
3133 / 2280 / 469
Регистрация: 26.03.2015
Сообщений: 8,876
25.12.2019, 10:29
Лучший ответ Сообщение было отмечено Olegzey как решение

Решение

Цитата Сообщение от Olegzey Посмотреть сообщение
я так и не понял идеи вашего решения
Общая идея такая же, как у Вас, но немного другая реализация.

Готовим массив промежуточных решний решений d. d[i, j] - количество разбиений числа j на слагаемые не больше i.

На слагаемые не больше 0 можно разбить только 0, поэтому нулевая строка (i = 0) выглядит так:
Code
1
1 0 0 0 0 0 0 0 0 0 0
Чтобы посчитать d[i, j], нужно к количеству разбиений числа j без использования слагаемого i прибавить количество разбиений числа j с использованием слагаемого i.
Количеству разбиений числа j без использования слагаемого i уже посчитано раньше и равно d[i-1,j].
Количество разбиений числа j с использованием слагаемого i равно количеству разбиений числа j-i без использования слагаемого i. Оно тоже уже посчитано раньше и равно d[i-1,j-i].

Чтобы посчитать всю строку d[i], нужно к строке d[i-1] прибавить строку d[i-1], сдвинутую на j позиций вправо.
Строка 1:
Code
1
2
3
  1 0 0 0 0 0 0 0 0 0 0
+ 0 1 0 0 0 0 0 0 0 0 0 
= 1 1 0 0 0 0 0 0 0 0 0
Строка 2:
Code
1
2
3
  1 1 0 0 0 0 0 0 0 0 0
+ 0 0 1 1 0 0 0 0 0 0 0
= 1 1 1 1 0 0 0 0 0 0 0
Строка 3:
Code
1
2
3
  1 1 1 1 0 0 0 0 0 0 0
+ 0 0 0 1 1 1 1 0 0 0 0 
= 1 1 1 2 1 1 1 0 0 0 0
Заметим, что для вычисления каждой строки мы используем только одну предыдущую. Поэтому весь массив хранить не нужно - достаточно хранить последнюю вычисленную строку массива. А так как для вычисления элемента мы используем только более левые элементы, то изменять её нужно справа налево.
На каждом шаге нужно печатать (или сохранять) последний элемент строки - количество разбиений числа N.

В императивном стиле код будет выглядеть так:
F#
1
2
3
4
5
6
7
let solve n = 
    let (d : int array) = Array.zeroCreate (n + 1)
    d.[0] <- 1
    for i = 1 to n do
        for j = n downto i do 
            d.[j] <- d.[j] + d.[j-i]
        printf "%i " d.[n]
0
0 / 0 / 0
Регистрация: 06.09.2018
Сообщений: 8
27.12.2019, 16:14  [ТС]
Shamil1, теперь понял, ваше решение однозначно быстрее, но для меня такой быстроты и не требуется, но тем не менее, огромное вам спасибо за потраченное время, теперь то можно и тему закрыть
0
Надоела реклама? Зарегистрируйтесь и она исчезнет полностью.
inter-admin
Эксперт
29715 / 6470 / 2152
Регистрация: 06.03.2009
Сообщений: 28,500
Блог
27.12.2019, 16:14
Помогаю со студенческими работами здесь

Разбиение числа n на слагаемые
Любое целое число n можно разложить на слагаемые. При это каждом наборе слагаемых все входящие в него числа упорядочены по неубыванию....

Разбиение числа на слагаемые
Задача Е. Разбиение на слагаемые Во входном файле задано число n (2&lt;=n&lt;=40). выведите в выходной файл все разбиения числа n на слагаемые...

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

Разбиение числа на слагаемые. Итеративное решение
На основании алгоритма Шеня на Паскале делаю аналогичное решение на Java &quot;Разбиение числа на слагаемые. Итеративное решение&quot;. ...

Сформировать массив Y=(yl,y2,...,ym), поместив в него в порядке убывания все различные (неповторяющиеся) числа
Помогите пожалуйста сделать лабораторную работу по матлабу • Разработать алгоритм, решающий задание по варианту из Приложения А. •...


Искать еще темы с ответами

Или воспользуйтесь поиском по форуму:
7
Ответ Создать тему
Новые блоги и статьи
Почему дизайн решает?
Neotwalker 09.01.2026
В современном мире, где конкуренция за внимание потребителя достигла пика, дизайн становится мощным инструментом для успеха бренда. Это не просто красивый внешний вид продукта или сайта — это. . .
Модель микоризы: классовый агентный подход 3
anaschu 06.01.2026
aa0a7f55b50dd51c5ec569d2d10c54f6/ O1rJuneU_ls https:/ / vkvideo. ru/ video-115721503_456239114
Owen Logic: О недопустимости использования связки «аналоговый ПИД» + RegKZR
ФедосеевПавел 06.01.2026
Owen Logic: О недопустимости использования связки «аналоговый ПИД» + RegKZR ВВЕДЕНИЕ Введу сокращения: аналоговый ПИД — ПИД регулятор с управляющим выходом в виде числа в диапазоне от 0% до. . .
Модель микоризы: классовый агентный подход 2
anaschu 06.01.2026
репозиторий https:/ / github. com/ shumilovas/ fungi ветка по-частям. коммит Create переделка под биомассу. txt вход sc, но sm считается внутри мицелия. кстати, обьем тоже должен там считаться. . . .
Расчёт токов в цепи постоянного тока
igorrr37 05.01.2026
/ * Дана цепь постоянного тока с сопротивлениями и напряжениями. Надо найти токи в ветвях. Программа составляет систему уравнений по 1 и 2 законам Кирхгофа и решает её. Последовательность действий:. . .
Новый CodeBlocs. Версия 25.03
palva 04.01.2026
Оказывается, недавно вышла новая версия CodeBlocks за номером 25. 03. Когда-то давно я возился с только что вышедшей тогда версией 20. 03. С тех пор я давно снёс всё с компьютера и забыл. Теперь. . .
Модель микоризы: классовый агентный подход
anaschu 02.01.2026
Раньше это было два гриба и бактерия. Теперь три гриба, растение. И на уровне агентов добавится между грибами или бактериями взаимодействий. До того я пробовал подход через многомерные массивы,. . .
Советы по крайней бережливости. Внимание, это ОЧЕНЬ длинный пост.
Programma_Boinc 28.12.2025
Советы по крайней бережливости. Внимание, это ОЧЕНЬ длинный пост. Налог на собак: https:/ / **********/ gallery/ V06K53e Финансовый отчет в Excel: https:/ / **********/ gallery/ bKBkQFf Пост отсюда. . .
КиберФорум - форум программистов, компьютерный форум, программирование
Powered by vBulletin
Copyright ©2000 - 2026, CyberForum.ru