找到x个子数组,使得这些子数组的总和最小

时间:2017-10-30 08:02:02

标签: java algorithm performance dynamic-programming

找出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}}

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

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

非常感谢任何帮助!

P.S。提醒一下,不要低估问题的复杂性。

2 个答案:

答案 0 :(得分:2)

这是我尝试应用DP方法。

<select class="form-control" onchange="showOptionsBelow(this)"> <option></option> <option value="First option">First option</option> <option value="Second option">Second option</option> <option value="Third option">Third option</option> </select> <div id="divA" style="display:none;">A</div> <div id="divB" style="display:none;">B</div>成为子阵列的最小总和,其中:

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

然后适用以下公式:

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))

答案 1 :(得分:0)

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

我花了很多时间在这上面,希望它运作良好。我评论它一如既往地说清楚。希望这有所帮助,我没有找到任何更有效的,抱歉。

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));
    }
}

[OLD NON WORKING SOLUTION]:反例:{1,100,2,2,100,1} x = 1,y = 2

我找到了这个alg。除非我弄错了,复杂性应该是O(Y×array lenght)。

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.

我知道这有点令人困惑,这甚至不是最糟糕的部分。 我做了以下代码来测试我的alg。这可能是我写过的最糟糕的代码。

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

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

如果你需要更多的澄清,请问(我会回复tommorow,因为在意大利这里是晚上;)

以下是代码:

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;
            }
        }
    }
}

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