У меня есть массив размера n
целочисленных значений и заданного числа S
,
1<=n<=30
Я хочу найти общее количество подпоследовательности такой, что для каждого подпоследовательности сумма элементов меньше чем S
,
Например: позволять n=3
, S=5
и элементы массива будут как {1,2,3}
тогда его общие подпоследовательности будут 7
как-
{1},{2},{3},{1,2},{1,3},{2,3},{1,2,3}
но необходимые подпоследовательности:
{1},{2},{3},{1,2},{1,3},{2,3}
то есть {1,2,3}
не берется, потому что его сумма элемента (1+2+3)=6
который больше чем S
то есть 6>S
, Другие взяты потому, что для других элементов подпоследовательностей сумма меньше S
,
Таким образом, общее количество возможных подпоследовательностей будет 6
,
Таким образом, мой ответ является подсчет, который6
,
Я пробовал рекурсивный метод, но сложность его времени 2^n
,
Пожалуйста, помогите нам сделать это за полиномиальное время.
Вы можете решить это за разумное время (возможно), используя псевдополиномиальный алгоритм для задачи о ранце, если числа ограничены положительными (или, технически, нулевыми, но я собираюсь предположить положительными). Это называется псевдополином, потому что он работает в nS
время. Это выглядит полиномом. Но это не так, потому что проблема имеет два параметра сложности: первый — это n, а второй — это «размер» S
количество цифр в S
Назовите его М. Так что этот алгоритм на самом деле n 2^M
,
Чтобы решить эту проблему, давайте определим двумерную матрицу A
, Она имеет n
строки и S
колонны. Мы скажем, что A[i][j]
количество подпоследовательностей, которые могут быть сформированы с использованием первого i
элементы и с максимальной суммой не более j
, Сразу отметим, что нижний правый элемент А является решением, т.е. A[n][S]
(да, мы используем индексирование на основе 1).
Теперь мы хотим формулу для A[i][j]
, Обратите внимание, что все подпоследовательности, использующие первый i
элементы либо включают в себя ith
элемент или нет. Количество подпоследовательностей, которые не являются просто A[i-1][j]
, Количество подпоследовательностей, которые делают это просто A[i-1][j-v[i]]
, где v[i]
это просто значение i-го элемента. Это потому, что, включив i-й элемент, нам нужно сохранить остаток суммы ниже j-v[i]
, Таким образом, добавив эти два числа, мы можем объединить подпоследовательности, которые содержат и не включают j-й элемент, чтобы получить общее число. Таким образом, это приводит нас к следующему алгоритму (примечание: я использую индексирование на основе нуля для элементов и i
, но 1 основано на j
):
std::vector<int> elements{1,2,3};
int S = 5;
auto N = elements.size();
std::vector<std::vector<int>> A;
A.resize(N);
for (auto& v : A) {
v.resize(S+1); // 1 based indexing for j/S, otherwise too annoying
}
// Number of subsequences using only first element is either 0 or 1
for (int j = 1; j != S+1; ++j) {
A[0][j] = (elements[0] <= j);
}
for (int i = 1; i != N; ++i) {
for (int j = 1; j != S+1; ++j) {
A[i][j] = A[i-1][j]; // sequences that don't use ith element
auto leftover = j - elements[i];
if (leftover >= 0) ++A[i][j]; // sequence with only ith element, if i fits
if (leftover >= 1) { // sequences with i and other elements
A[i][j] += A[i-1][leftover];
}
}
}
Запуск этой программы, а затем вывод A[N-1][S]
дает 6 по мере необходимости. Если эта программа не работает достаточно быстро, вы можете значительно улучшить производительность, используя один вектор вместо вектора векторов (и вы можете сэкономить немного места / перфорации, не тратя впустую столбец для 1-индекса, как я сделал ).
Да. Эта проблема может быть решена за псевдополиномиальное время.
Позвольте мне переопределить постановку задачи как «Подсчитать количество подмножеств, которые имеют SUM <= К «.
Ниже приведено решение, которое работает в O (N * K),
где N — количество элементов, а K — целевое значение.
int countSubsets (int set[], int K) {
int dp[N][K];
//1. Iterate through all the elements in the set.
for (int i = 0; i < N; i++) {
dp[i][set[i]] = 1;
if (i == 0) continue;
//2. Include the count of subsets that doesn't include the element set[i]
for (int k = 1; k < K; k++) {
dp[i][k] += dp[i-1][k];
}
//3. Now count subsets that includes element set[i]
for (int k = 0; k < K; k++) {
if (k + set[i] >= K) {
break;
}
dp[i][k+set[i]] += dp[i-1][k];
}
}
//4. Return the sum of the last row of the dp table.
int count = 0;
for (int k = 0; k < K; k++) {
count += dp[N-1][k];
}
// here -1 is to remove the empty subset
return count - 1;
}