Algorithm 给定一组n个整数,用sum>;列出所有可能的子集=K

Algorithm 给定一组n个整数,用sum>;列出所有可能的子集=K,algorithm,Algorithm,给定数组形式的未排序整数集,查找其和大于或等于常量整数k的所有可能子集, 我们的集合是{1,2,3},k=2 可能的子集:- {2}, {3}, {1,2}, {1,3}, {2,3}, {1,2,3} 我只能想到一个简单的算法,它列出集合的所有子集并检查子集的和是否大于等于k,但它是一个指数算法,列出所有子集需要O(2^N)。我可以用动态规划在多项式时间内求解它吗 我可以用动态规划在多项式时间内求解它吗 不。这个问题比@amit(在评论中)提到的还要难。寻找是否存在一个和特定k

给定数组形式的未排序整数集,查找其和大于或等于常量整数k的所有可能子集, 我们的集合是{1,2,3},k=2

可能的子集:-

 {2},
 {3},
 {1,2},
 {1,3},
 {2,3}, 
 {1,2,3}
我只能想到一个简单的算法,它列出集合的所有子集并检查子集的和是否大于等于k,但它是一个指数算法,列出所有子集需要O(2^N)。我可以用动态规划在多项式时间内求解它吗

我可以用动态规划在多项式时间内求解它吗


不。这个问题比@amit(在评论中)提到的还要难。寻找是否存在一个和特定k求和的子集是一个NP难问题。相反,你要问的是有多少解等于一个特定的k,这是一个更难的问题。此外,您的精确问题稍微困难一些,因为您不仅要计算,还要枚举k和目标如果k为0,并且集合的每个元素都为正,则您别无选择,只能输出每个可能的子集,因此此问题的下界为O(2N)--产生输出所需的时间


除非你对k值有更多的了解,但你没有告诉我们,否则没有比检查每个子集更快的通用解决方案。

列出所有子集仍然是
O(2^N)
,因为在最坏的情况下,你可能仍然必须列出除空子集之外的所有子集

动态规划可以帮助您计算具有
sum>=K

您可以自下而上跟踪从范围
[1..K]
到某个值的子集总数。这样的方法将是
O(N*K)
,这将只适用于较小的
K

用一个例子最好地说明了动态规划解决方案的思想。考虑一下这种情况。假设您知道在由第一个
i
元素组成的所有集合中,您知道
t1
2
t2
3
。假设下一个
i+1
元素是
4
。给定所有现有集合,我们可以通过追加元素
i+1
或将其省略来构建所有新集合。如果我们不考虑它,我们会得到求和为
2
t1
子集和求和为
3
t2
子集。如果我们附加它,那么我们获得了求和为
6
(2+4)的
t1
子集和求和为
7
(3+4)的
t2
,以及一个只包含求和为4的
i+1
子集。这就给出了由第一个
i+1
元素组成的
(2,3,4,6,7)
子集的数目。我们继续,直到
N

在伪代码中,这可能类似于:

int DP[N][K];
int set[N];

//go through all elements in the set by index
for i in range[0..N-1]
   //count the one element subset consisting only of set[i]
   DP[i][set[i]] = 1

   if (i == 0) continue;

   //case 1. build and count all subsets that don't contain element set[i]
   for k in range[1..K-1]
       DP[i][k] += DP[i-1][k]

    //case 2. build and count subsets that contain element set[i]
    for k in range[0..K-1] 
       if k + set[i] >= K then break inner loop                     
       DP[i][k+set[i]] += DP[i-1][k]

//result is the number of all subsets - number of subsets with sum < K
//the -1 is for the empty subset
return 2^N - sum(DP[N-1][1..K-1]) - 1
intdp[N][K];
整数集[N];
//按索引遍历集合中的所有元素
对于[0..N-1]范围内的i
//计算仅由集合[i]组成的一个元素子集
DP[i][set[i]]=1
如果(i==0)继续;
//案例1。生成并计算不包含元素集[i]的所有子集
对于[1..k-1]范围内的k
DP[i][k]+=DP[i-1][k]
//案例2。生成并计算包含元素集[i]的子集
对于[0..k-1]范围内的k
如果k+集[i]>=k,则中断内部循环
DP[i][k+集[i]+=DP[i-1][k]
//结果是所有子集的数目-总和小于K的子集数目
//-1表示空子集
返回2^N-和(DP[N-1][1..K-1])-1

如果您对打印或列出所有这些子集感兴趣,那么在最坏的情况下,您可能仍然需要列出
2^N-1
(除空之外的所有子集)。然而,你可以用多项式中的动态规划来计算有多少个子集。@cyon,我们如何用动态规划来计算这样的子集的数量?找到是否有一个子集和
k
,这是NP难问题(子集和问题)——因此,这个问题也是如此。既然你想要的是实际的集合,在我看来,用蛮力生成所有的子集是可行的。(可能会使用分支和绑定技术添加一些优化,但仅此而已,IMO)@amit子集求和问题指的是找到一个子集,该子集的求和正好等于
k
,而不是至少等于
k
。找到一个总和至少为
k
的子集是O(n)(只要把所有的东西加起来,看看总和是否足够大)。@dfan但问题是找到所有大于/等于
k
的子集。要做到这一点,您需要找到求和到
k
的所有子集。找到它们是NP难的。你能告诉我怎么做-->求和(DP[1…K]@rasooll我的意思是
sum=DP[1]+DP[2]+..+DP[K]
DP是一个二维数组,所以DP[0]+…+DP[K]不是真的吗think@Rasoolll哦,是的,抱歉我没注意到。
sum=DP[N-1][1]+DP[N-1][2]+..+DP[N-1][K-1]
。因为您试图查找最多由
N
组成的集合(
N-1
,因为从0索引)多少和等于
1,2,3,…K-1
。当你计算出多少时,你从所有子集中减去这个,得到有多少和等于
K,K+1
,等等。我的错误是没有在代码中很好地解释它。让我们来看看。