【问题标题】:Find an x amount of subarrays such that the total sum of those subarrays is minimum [closed]找到 x 个子数组,使这些子数组的总和最小 [关闭]
【发布时间】:2017-10-30 08:02:01
【问题描述】:

找到 x 个不重叠且连续的子数组,使得这些子数组的总和最小,并且所有元素的计数为 y。

Example 1:
Input: {2,1, 10, 40, 5, 6} , x=2, y=4
Output: {{2,1},{5,6}}

Example 2:
Input: {2,1, 10, 40, 5, 6} , x=1, y=2
Output: {{2,1}}

Example 3:
Input: {2,1, 10, 40, 5, 6} , x=1, y=3
Output: {{2,1,10}}

Example 4:
Input: {2,1, 10, 40, 5, 6} , x=1000, y=3
Output: {{2},{1},{5}} or {{2,1},{5}}

我已经通过互联网搜索过,但我找不到类似的问题。所以,我做了自己的算法。不幸的是,时间复杂度是指数级的。我不会给出我的解决方案,因为我已经创建了一个狭隘的视野,并且想从头开始有新的想法。

所以,这是我的问题:你知道一种算法可以尽可能高效地解决这个问题吗?

任何帮助将不胜感激!

附言提醒一下,不要低估问题的复杂性。

【问题讨论】:

  • 您应该定义 x 和 y。目前尚不清楚它们是什么
  • 查看“背包问题”
  • 什么是x?子数组个数的上限?
  • 正如其他人所说,您对x 的定义与上一个示例不匹配。你说这是子数组的数量,但在最后一个例子中不是。对于定义不明确的问题,你不能指望得到有用的答案。
  • x 是要查找的最大子数组数吗?

标签: java algorithm performance dynamic-programming


【解决方案1】:

这是我应用 DP 方法的尝试。

M(I, Y, X, L) 为子数组的最小总和,其中:

  • I - 我们使用原始数组 ARR 的第一个 I 元素
  • Y - 子数组中所有元素的计数
  • X - 子数组数量的上限
  • L = 1 如果最后一个 (I-th) 元素包含在形成最小值的子数组之一中,否则 L = 0

那么以下公式适用:

M(I, Y, X, 1) = ARR[I] + MIN(M(I-1, Y-1, X, 1), M(I-1, Y-1, X-1, 0))

M(I, Y, X, 0) = MIN(M(I-1, Y, X, 1), M(I-1, Y, X, 0))

【讨论】:

    【解决方案2】:

    [使用动态编程的新解决方案]

    我在这方面花了很多时间,希望它运作良好。我评论它是为了像往常一样清楚。希望这会有所帮助,我没有找到更有效的方法,抱歉。

    package temp2;
    
    public class Main {
        private static int
                X=1, //This i understood is the max number of subarray. Change it to find counterexample.
                Y=2, //This is the number of elements you want as result. Change it to find counterexample.
                VERY_LARGE_NUMBER=1000000;
    
        private static int array[] = {1, 100, 2, 2, 100, 1}; //just your array. Change it to find counterexample. Remember to leave the bounds (9999) as i explained in the answer.
    
    
    
        public static void main(String args[]){
            System.out.println("Obtained result: "+alg(array.length-1, 0, 0, 0, false));
        }
    
        public static int alg(int index, int partialSum, int numberOfUsedArrays, int numberOfUsedElements, boolean beforeIsInResults){
    
            /**
             * If the remaning number to analize are equal than the max number of elements minus the number of elements found AND
             * i can add them to the results adding a new subarray OR using the adjacent one, i add all the value to the result sum.
             */
            if(index+1+numberOfUsedElements == Y && (numberOfUsedArrays<X || (beforeIsInResults && numberOfUsedArrays<=X))){
                int summ = 0;
                for(int i=0; i<=index; i++)
                    summ+=array[i];
                return summ+partialSum;
            }
    
            /**
             * If i don't have any subarray to create or to use (if is possible to connect to the adjacent) AND i don't enough elements
             * in the computed solution i don't consider this solution.
             */
            if((((numberOfUsedArrays > X && beforeIsInResults) || (numberOfUsedArrays >= X && !beforeIsInResults)) && numberOfUsedElements < Y )){   //Old condition i think is no more needed: || (index+1+numberOfUsedElements == Y && ((numberOfUsedArrays > X && beforeIsInResults) || (numberOfUsedArrays >= X && !beforeIsInResults)))){
                return VERY_LARGE_NUMBER;
            }
    
            /**
             * If the index is out of bound OR i don't have any more subarrays left OR i reach the max number of element of the result i return the computed solution.
             */
            if( index < 0 || ((numberOfUsedArrays > X && beforeIsInResults) || (numberOfUsedArrays >= X && !beforeIsInResults)) || numberOfUsedElements >= Y )
                return partialSum;
    
    
            /**
             * I check if the best solution contains OR not contain the selected index. The only difference from if to else is that in case in which i choose to
             * add the element to the result is the element computed before (index+1) was selected no new array has been used else i need to update the number of array used.  
             */
            if(beforeIsInResults)
                return Math.min(
                        alg(index-1, partialSum+array[index], numberOfUsedArrays, numberOfUsedElements+1, true),
                        alg(index-1, partialSum, numberOfUsedArrays, numberOfUsedElements, false));
            else
                return Math.min(
                        alg(index-1, partialSum+array[index], numberOfUsedArrays+1, numberOfUsedElements+1, true),
                        alg(index-1, partialSum, numberOfUsedArrays, numberOfUsedElements, false));
        }
    }
    

    [旧的非工作解决方案]: 反例:{1, 100, 2, 2, 100, 1} x = 1, y = 2

    我找到了这个算法。除非我弄错了,否则复杂度应该是 O(Y × 数组长度)。

    n.b.所有这一切都假设您的 X 变量表示“子数组的最大数量”

    Start: Find min in array not in result
           add it to result
           did we reach the max number of result?
           (Yes: end and print results) (No: go to Continue)
    
    Continue: Compute min subarrays in the results.
              Is it equals to the max subarray (`Y`)?
              (No: go to Start) (Yes:Continue2)
    
    Continue2: Find minimum value near one subarray.
               go to Continue.
    

    我知道这有点令人困惑,但这还不是最糟糕的部分。 我制作了以下代码来测试我的算法。这可能是我写过的最糟糕的代码了。

    我试图保持简单,避免任何“案例特殊控制”,例如在某些情况下,如果应该控制以避免任何索引超出范围异常(而不是这个,因为我很懒,我只是放置了一些大的无用的值)。

    我已经尝试了你所有的组合并且它有效。我没有找到任何反例。

    如果您需要更多说明,请询问(我明天会回复,因为在意大利这里是晚上;))

    代码如下:

    public class Main {
        public static void main(String[] args){
            int     X=1000, //This i understood is the max number of subarray. Change it to find counterexample.
                    Y=3, //This is the number of elements you want as result. Change it to find counterexample.
                    temp, //This is just a value i used to store the minimum during the search along the array.
                    tempIndex, //This is the one that keeps the index of temp.
                    subarray=0, //This value will contain the minimum number of subarrays formed by the results. Since at the start there are no results, there are no subarray either. (to compare with X)
                    resultsSize=0; //This will used to store the number of results temporary found (to compare with Y)
    
            int array[] = {9999,2,1,10,40,5,6,9999}; //just your array. Change it to find counterexample. Remember to leave the bounds (9999) as i explained in the answer.
    
            /*If my professor saw me use two parallel arrays instead of an array of objects 
             *I think he might kill me, but as I said, I'm lazy and this code just serves to make an example
             */
            boolean used[] = {false, false, false, false, false, false, false, false}; //Just used to chceck if one value is already used in results.
    
            while(true){
                try{
    
                    //The following code just find the minimum element not used.
                    temp=9998;
                    tempIndex=-1;
                    for(int i=0; i<array.length; i++){
                        if(array[i]<temp && !used[i]){
                            temp=array[i];
                            tempIndex=i;
                        }
                    }
    
                    //The following code add the found number to the results (just print it)
                    used[tempIndex] = true;
                    resultsSize++;
                    System.out.print(temp+" ");
    
                    //This is one of the two way to return with success. Basically we return when we found Y results.
                    if(resultsSize == Y ) {
                        System.out.println("\nDone.");
                        return;
                    }
    
                    /*When i add an element to the result 3 things may happen.
                     *The result isn't near to any result, so it would create a new subarray
                     *The result is near to just one result, no new subarray created
                     *The result is just in the middle of two other result, adding it will result in fusion on two subarrays into one.
                     *The following code just use this rule to compute the subarray number
                     */
                    if(used[tempIndex-1] && used[tempIndex+1]){ //third case
                        subarray--;
                    }else if(!used[tempIndex-1] && !used[tempIndex+1]){ //first case
                        subarray++;
                    }
    
                    /*The following code will be executed only IF we reach the limit of subarrays. If so, he just try to add the smallest element without
                     *any subarray increment. If the subarrays get decremented (third case of the comment above) the while ends and the control returns back to the
                     *up side of this function early discussed.
                     */
                    while(subarray == X){
    
                        //The following code just find the minimum element not used.
                        temp=9998;
                        tempIndex=-1;
                        for(int i=0; i<array.length; i++){
                            if(array[i]<temp && !used[i] && (used[i-1] || used[i+1])){
                                temp=array[i];
                                tempIndex=i;
                            }
                        }
    
                        //If this is true there are no minimum element near results which practically means that your 'Y' value is bigger than the length of the array
                        if(temp==9998){
                            System.out.println("\nYou shloud not ever come here.\ncheck if your 'Y' value is bigger than the length of the array");
                            return;
                        }
    
                        //The following code add the found number to the results (just print it)
                        used[tempIndex] = true;
                        resultsSize++;
                        System.out.print(temp+" ");
    
                        //This is one of the two way to return with success. Basically we return when we found Y results.
                        if(resultsSize == Y ){
                            System.out.println("\nDone.");
                            return;
                        }
    
                        //The following code checks with the new added result the number of subarrays get decremented.
                        //Remember that if subarrays get decremented the alg go back to the first part and search for minimum even non near to an existing subarray.
                        if(used[tempIndex-1] && used[tempIndex+1]){
                            subarray--;
                        }
                    }
                } catch(Throwable e){ //Used for debugging, no more needed.
    
                    e.printStackTrace();
                    return;
                }
            }
        }
    }
    

    让我知道这是否有用或只是浪费时间;)

    【讨论】:

    • 对不起,但这显然是错误的贪心算法。反例 {1, 100, 2, 2, 100, 1}, x = 1, y = 2。
    • 你说得对,我会努力解决这个问题
    • 我怀疑贪心算法能解决这个问题
    • 是的,我也开始怀疑它了 XD 实际上我正在尝试用不同的方法找到一些东西
    猜你喜欢
    • 2020-10-21
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2015-08-09
    • 2022-10-12
    • 2022-12-12
    • 2014-08-26
    • 2017-01-22
    相关资源
    最近更新 更多