找到总和为N的所有M长度正整数集

时间:2014-03-24 09:07:56

标签: algorithm

我试图解决的问题是我们如何找到所有整数集[a1,a2,...,am]以便

a1 + a2 + ... + am = N

并使用约束ai >= 1

例如,如果M = 4,N = 7则有三个答案

[1,1,1,4]
[1,1,2,3]
[1,2,2,2]

3 个答案:

答案 0 :(得分:1)

由于您必须打印总和为N的所有集合。您可以使用递归的完整搜索算法。在下面的代码中,M是集合中的数字数,N是所需的总和。

        int M;
        int N;

        void run(){
            M = 4;
            N = 7;
            int[] arr = new int[M];
            print(arr, 0, N, 1);
        }

// req holds the required sum for the numbers in the array from arr[from] 
// to arr[M-1]. 
// "last" holds the last value that we had put in the array.
// The first call to the array will be with last=1.

        void print(int[] arr, int from, int req, int last){

           // Reached the end of the array and sum required 0.

            if(from==M && req==0){
                System.out.println(Arrays.toString(arr));
                return;
            }



           // Either reached the end of the array but sum is not equal to N
           // Or if we have not reached the end of the array but sum has already 
           // become more than or equal to N.

            if(from==M || req<=0){
                return;
            }



            for(int i=last; i<=req; i++){
                arr[from] = i;
                print(arr, from+1, req-i, i);
            }
        }

M = 4且N = 7的输出:

[1, 1, 1, 4]
[1, 1, 2, 3]
[1, 2, 2, 2]

M = 3且N = 10的输出:

[1, 1, 8]
[1, 2, 7]
[1, 3, 6]
[1, 4, 5]
[2, 2, 6]
[2, 3, 5]
[2, 4, 4]
[3, 3, 4]

答案 1 :(得分:1)

回答链接中的问题,刚接受。

这个想法很简单,假设我们知道每个部分的最大值是X,并且我们想找到一种方法来划分这些文件柜以实现这一点,我们可以贪图地将它们划分为:

  • 从第一个机柜开始,遍历每个机柜,直到从第一个机柜到ith机柜的总数大于X。所以这是第一部分,类似地,我们可以选择其他部分。这种贪婪总能找到解决方案(如果存在)。

最后,我们可以使用二分搜索来调整X的值,如果我们能找到一种分隔橱柜的方法,可以减少X,如果我们找不到,可以增加X.

以下是Java中的代码:

public class FairWorkload {

public int getMostWork(int[] folders, int workers) {
    int[] data = new int[folders.length];
    data[0] = folders[0];
    for (int i = 1; i < data.length; i++) {
        data[i] = data[i - 1] + folders[i];
    }
    if (workers == 1) {
        return data[data.length - 1];
    }
    int start = 0;
    int end = data[data.length - 1];
    int result = Integer.MAX_VALUE;
    while (start <= end) {
        int mid = (start + end) / 2;
        int index = 0;            
        for (int k = 0; k < workers && index < data.length; k++) {
            int less = index > 0 ? data[index - 1] : 0;
            int temp = index;
            for (int i = index; i < data.length; i++) {
                if (data[i] - less <= mid) {
                    temp = i;
                } else {
                    break;
                }
            }
           // System.out.println(data[temp] - less + " " + mid);
            if(data[temp] - less > mid){
                index = -1;
                break;
            }
            index = temp + 1;

        }
        //System.out.println(mid + " " + index);
        if (index != data.length) {
            start = mid + 1;
        } else {
            result = Math.min(result, mid);
            end = mid - 1;
        }
    }
    return result;
}

public static void main(String[] args) {
    int[] data = {1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1000};
    System.out.println(new FairWorkload().getMostWork(data, 2));
}
}

答案 2 :(得分:0)

一种可能的解决方案是使用计数技术,找到满足[i] <= [length-1] - 2的最右边的术语,然后尽可能地将所有其他术语展平到右边,保持[i] <= a [i + 1]。

import java.util.Arrays;

public class test {

    public static void main(String[] args) {
        test t = new test();
        t.go();
    }

    private void go() {
        int w = 3;
        int sum = 10;

        int[] terms = new int[w];
        for (int i = 0; i < terms.length; i++) {
            terms[i] = 1;
        }

        terms[w-1] = sum - w + 1;
        System.out.println(Arrays.toString(terms));

        for (int i = right_index(terms); i>=0; i = right_index(terms)) {
            terms[i]++;
            int a = terms[i];
            int overflow = -1;

            // balance all the terms to the right
            for (int j = i+1; j < terms.length-1; j++) {
                overflow += terms[j] - a;
                terms[j] = a;
            }

            terms[terms.length-1] += overflow;
            System.out.println(Arrays.toString(terms));
        }
    }

    // find the rightmost index i, where [i] <= [ia.length-1] - 2
    private int right_index(int[] ia) {

        int max = ia[ia.length-1];

        for (int i = ia.length - 1; i >= 0; i--) {
            if (ia[i] <= max - 2)
                return i;
        }

        return -1;
    }
}