在Java中,我应该如何找到一个数组元素的最接近(或等于)可能和一个特定的值K?
例如,对于数组{19,23,41,5,40,36}和K = 44,最接近的可能之和为23 + 19 = 42。 我一直挣扎在这几个小时; 我几乎一无所知动态规划。 顺便说一句,该数组只包含正数。
在Java中,我应该如何找到一个数组元素的最接近(或等于)可能和一个特定的值K?
例如,对于数组{19,23,41,5,40,36}和K = 44,最接近的可能之和为23 + 19 = 42。 我一直挣扎在这几个小时; 我几乎一无所知动态规划。 顺便说一句,该数组只包含正数。
您通常会使用动态编程这样的问题。 然而,这基本上归结为保持一组可能的和的和将输入值一个接一个,如在下面的代码,并且具有相同的渐近运行时间: O(n K)
其中n
是您输入的大小阵列和K
是目标值。
下面的版本的常量可能是更大的,但是,但是我觉得代码更容易跟踪,比动态规划版本会。
public class Test {
public static void main(String[] args) {
int K = 44;
List<Integer> inputs = Arrays.asList(19,23,41,5,40,36);
int opt = 0; // optimal solution so far
Set<Integer> sums = new HashSet<>();
sums.add(opt);
// loop over all input values
for (Integer input : inputs) {
Set<Integer> newSums = new HashSet<>();
// loop over all sums so far
for (Integer sum : sums) {
int newSum = sum + input;
// ignore too big sums
if (newSum <= K) {
newSums.add(newSum);
// update optimum
if (newSum > opt) {
opt = newSum;
}
}
}
sums.addAll(newSums);
}
System.out.println(opt);
}
}
编辑
因为我只是声称对运行时间的简短的笔记可能是有用的, O(n K)
没有正当理由。
显然,初始化和打印结果只是需要一定的时间,所以我们应该分析双回路。
外环运行在所有输入,所以它的主体中执行n
倍。
内环上的所有款项,运行至今,这可能是在理论上一个指数。 但是 ,我们使用的上限的K
,所以在所有的值sums
的范围是[0, K]
因为sums
是一组,它含有至多K+1
的元件。
内环内所有的计算采取恒定时间,所以总循环花费O(K)
该组newSums
还含有至多K+1
的元件,由于相同的原因,所以addAll
到底需要O(K)
为好。
结束语:外循环执行n
次。 循环体花费O(K)
因此,该算法在运行O(n K)
编辑2
在关于如何还发现,导致最佳的总和要素的要求:
子列表的总和 - - 而不是保持一个整数的轨道,你也应该跟踪的子表本身。 如果你创建一个新的类型(没有getter / setter方法保持示例简洁)这是一个相对简单:
public class SubList {
public int size;
public List<Integer> subList;
public SubList() {
this(0, new ArrayList<>());
}
public SubList(int size, List<Integer> subList) {
this.size = size;
this.subList = subList;
}
}
初始化现在变成:
SubList opt = new SubList();
Set<SubList> sums = new HashSet<>();
sums.add(opt);
在内环sums
需要一些小的调整,以及:
for (Integer input : inputs) {
Set<SubList> newSums = new HashSet<>();
// loop over all sums so far
for (SubList sum : sums) {
List<Integer> newSubList = new ArrayList<>(sum.subList);
newSubList.add(input);
SubList newSum = new SubList(sum.size + input, newSubList);
// ignore too big sums
if (newSum.size <= K) {
newSums.add(newSum);
// update optimum
if (newSum.size > opt) {
opt = newSum;
}
}
}
sums.addAll(newSums);
}
你可以把它看成是n-choose-k
问题的所有可能k
所以复杂性是指数 。
K
。 该组应该包括i
数字, i=1; i<=N; i++
i=1; i<=N; i++
i=1; i<=N; i++
。 为了实现这一点,每个i
只是把所有的n-choose-i
的组合阵列中的数字。 finalResult
与数字的迄今为止发现的最好的一套和它们的和可变的。 finalResult
并在必要时进行更新。 这让我想起了的背包问题 ,所以你可能想看看它。
我想说的第一个数组排序。 然后,你的例子是:ARR = {5,19,23,36,40,41}。 然后:1)取的常用3 [0]和常用3 [I],其中i = arr.Size。 概括并记录之和K之间的差,如果该总和大于K. 2)如果总和>该k低,执行步骤1,但不是ARR [I]中,使用ARR [I-1],因为我们要降低我们的总和。 如果总和<K,执行步骤1,但不是ARR [0],使用ARR [1],因为我们要提高我们的总和。 不断重复步骤2,通过增加或减少的索引,直到两个元件的索引相等。 然后,我们知道对元素导致的总和和K之间的最小差异
----------------在溶液中为编辑单元的任意数量----------------
我相信你可能需要一棵树。 下面是我在想什么:
1)选择一个号码作为顶级节点。
2)对于该集合中的每个数字,创建一个子节点,而对于所创建的每个分支,计算该分支的总和。
3)如果总和小于K,我们再次分支,在集合的所有元素创建子节点。 如果总和大于K,我们停止,保持总和与K(如果总和<K)之间的差异。 如果我们找到一个更好的和一个分支,那么我们保持这一分支。 重复这个过程,直到所有分支进行分支。
执行步骤1-3不同topnodes。