Java 如何找到数组';将元素转换为特定值?

Java 如何找到数组';将元素转换为特定值?,java,arrays,algorithm,dynamic-programming,Java,Arrays,Algorithm,Dynamic Programming,在Java中,如何找到与特定值K最接近(或相等)的数组元素和 例如,对于数组{19,23,41,5,40,36}和K=44,最接近的可能和是23+19=42。 我已经为此奋斗了几个小时;我对动态规划几乎一无所知。顺便说一句,数组只包含正数。我想先对数组进行排序。那么您的示例是:arr={5,19,23,36,40,41}。 然后: 1) 取arr[0]和arr[i],其中i=arr.Size。如果总和小于K,则求和并记录总和与K之间的差值。 2) 如果sum>K,则执行步骤1,但不使用arr[i

在Java中,如何找到与特定值K最接近(或相等)的数组元素和

例如,对于数组{19,23,41,5,40,36}和K=44,最接近的可能和是23+19=42。
我已经为此奋斗了几个小时;我对动态规划几乎一无所知。顺便说一句,数组只包含正数。

我想先对数组进行排序。那么您的示例是:arr={5,19,23,36,40,41}。 然后: 1) 取arr[0]和arr[i],其中i=arr.Size。如果总和小于K,则求和并记录总和与K之间的差值。 2) 如果sum>K,则执行步骤1,但不使用arr[i],而是使用arr[i-1],因为我们希望降低sum。 如果sum ----------------针对解决方案中任意数量的元素进行编辑----------------

我相信你可能需要一棵树。以下是我的想法:

1) 选择一个数字作为顶部节点

2) 对于集合中的每个编号,创建一个子节点,对于创建的每个分支, 计算该分支的总和

3) 如果总和小于K,我们将再次分支,为集合中的所有元素创建子节点。如果和大于K,我们停止,保持和与K之间的差(如果和
使用不同的TopNode执行步骤1-3。

您通常会使用动态编程解决此类问题。然而,这本质上归结为保留一组可能的和,并将输入值逐个相加,如以下代码所示,并且具有相同的渐近运行时间:
O(nk)
,其中
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);
    }
}
现在初始化为:

SubList opt = new SubList();

Set<SubList> sums = new HashSet<>();
sums.add(opt);  

对于所有可能的
k
问题,您可以将其视为一个
n-choose-k
问题,因此复杂性很高

  • 查找一组总和最多为
    K
    的数字。对于
    i=1,集合应包括
    i
    数字;i
    private int closestSum(int[]a,int num){
    int k=a.长度-1;
    整数和=0;
    数组。排序(a);
    while(a[k]>num){
    k--;
    }
    
    对于(inti=0;这是一个公平的问题,但可能更适合这里:5+40不是最接近的总和吗?我的意思是,总和将小于或等于KIs,你可以加起来的数字有一个限制吗?例如,你能不能每个总和只使用两个元素,或者你能不能对所有元素都使用总和?如果我理解正确,这将不会是正确地解决他的问题;举个例子[1,2,10]搜索13。@Calpis,但是如果结果中有两个以上的数字呢?哦,对不起,我没有意识到这是一个任意的数字。让我重新思考一下。我阅读了你的解决方案并试图理解,也许我的问题很愚蠢,但我可以看到你在最终解决方案中添加了一个数字,但我看不到的是你在哪里排除我希望,在某个时刻,你应该意识到,为了得到更好的解决方案,应该排除一个包含的数字,不是吗?@zafeiris.m我只跟踪和,而不是它们组成的值。基本上,在外循环的迭代
    I
    之后(超过
    输入
    ),集合
    总和
    包含所有可以用第一个
    i
    输入值进行的总和。为了确保该集合的大小不会呈指数增长,我添加了
    上限
    ,以确保
    总和
    最多包含
    2*K
    值。但如果最佳解决方案不包括第一个值,该怎么办
    i
    元素,但是,比如说,第1、第4和第6个?@Heuster谢谢,我现在可以看到了,很好的解决方案!但我不理解的是最坏情况的复杂性。在您的评论示例中(在任何示例中,大多数元素都将包含在结果中),我看到结果集每次都加倍,导致指数复杂性,不是吗?告诉我我是否错了。更新:空间复杂性也是指数的,因为你保留每个子结果,对吗?@zafeiris.m我添加了一个关于运行时间的注释。诀窍在于
    上限
    ,它使
    的大小在
    K
    中保持线性>.
    SubList opt = new SubList();
    
    Set<SubList> sums = new HashSet<>();
    sums.add(opt);  
    
    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);
    }
    
    private int closestSum(int[] a, int num){
        int k=a.length-1;
        int sum=0;
        Arrays.sort(a);
    
        while(a[k]>num){
            k--;
        }
        for(int i=0;i<k;i++){
            for(int j=i+1;j<=k;j++){
                if(a[i]+a[j]<=num && a[i]+a[j]>sum)
                    sum = a[i]+a[j];
            }
        }
        return sum;
    }