计算给定数组的子序列数,使其总和小于或等于给定数量?

时间:2017-05-09 07:54:52

标签: c++ arrays algorithm subsequence

我有一个大小为n的整数值和给定数字S的数组。

1<=n<=30

我想找到子序列的总数,使得每个子序列元素的总和小于S例如:n=3S=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。 请帮助我们在多项式时间内完成。

2 个答案:

答案 0 :(得分:2)

你可以在合理的时间(可能)使用伪多项式算法解决背包问题,如果数字被限制为正(或者,技术上,零,但我将假设为正)。它被称为伪多项式,因为它在nS时间内运行。这看起来是多项式的。但事实并非如此,因为问题有两个复杂性参数:第一个是n,第二个是S的“大小”,即S中的位数,称之为M.所以这个算法实际上是n 2^M

要解决这个问题,让我们定义一个二维矩阵A。它有n行和S列。我们会说A[i][j]是可以使用第一个i元素形成的子序列数,最大总和最多为j。立即观察到A的右下角元素是解,即A[n][S](是的,我们使用的是基于1的索引)。

现在,我们需要A[i][j]的公式。请注意,使用第一个i元素的所有子序列都包含ith元素,或者不包含A[i-1][j]元素。不包含的子序列数仅为A[i-1][j-v[i]]。执行的子序列数仅为v[i],其中j-v[i]只是第i个元素的值。那是因为通过包含第i个元素,我们需要将总和的余数保持在i以下。因此,通过添加这两个数字,我们可以组合包含第j个元素和不包含第j个元素的子序列来获取总数。因此,这引出了以下算法(注意:我对元素和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]; } } } 使用1):

A[N-1][S]

运行此程序,然后输出select sum(case when col2 = 1 then 1 end) * 1.0 / count(distinct col1) from YourTable ,根据需要产生6。如果这个程序运行速度不够快,你可以通过使用单个向量而不是向量向量来显着提高性能(并且你可以通过不浪费一个列来保存一些空间/性能,以便为1指数,就像我做的那样)。

答案 1 :(得分:-1)

是。这个问题可以在伪多项式时间内解决。

让我将问题陈述重新定义为“计算具有SUM&lt; = K”的子集的数量

以下是一个适用于 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;
}