c – 计算给定数组的子序列数,使得它们的总和小于或等于给定数量
我有一个大小为n的整数值和给定数字S的数组.
1<=n<=30 我想找到子序列的总数,使得对于每个子序列,元素sum小于S. {1},{2},{3},{1,2},{2,3} 但是,所需的子序列是: {1},3} 因为其元素和是(1 2 3)= 6,其大于S,即6> S,所以不采用{1,3}.取其他因为,对于其他子序列,元素和小于S. 我尝试过递归方法但其时间复杂度为2 ^ n. 解决方法
你可以在合理的时间(可能)使用伪多项式算法解决背包问题,如果数字被限制为正(或者,技术上,零,但我将假设为正).它被称为伪多项式,因为它在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元素的所有子序列要么包含第i个元素,要么不包含.不具有的子序列的数量仅为A [i-1] [j].做的子序列的数量只是A [i-1] [j-v [i]],其中v [i]只是第i个元素的值.那是因为通过包含第i个元素,我们需要将总和的余数保持在j-v [i]之下.因此,通过添加这两个数字,我们可以组合包含第j个元素和不包含第j个元素的子序列来获取总数.所以这导致我们使用以下算法(注意:我对元素和i使用基于零的索引,但是基于j使用1): std::vector<int> elements{1,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指数,就像我做的那样). (编辑:李大同) 【声明】本站内容均来自网络,其相关言论仅代表作者个人观点,不代表本站立场。若无意侵犯到您的权利,请及时与联系站长删除相关内容! |