找到数字N之和的所有唯一组合

时间:2012-06-14 07:26:51

标签: algorithm backtracking

Print all combinations of a number N, as a sum of positive integers?
They should be unique

示例

3 =
1 2

1 1 1

4=
3 1

2 2

1 1 2

1 1 1 1

我已经使用回溯为这个做了一个解决方案,但问题是它也提供重复,例如3

我正在

1 1 2

2 1 1

如何才能获得独特的组合?

许多人提前感谢

6 个答案:

答案 0 :(得分:5)

当您创建背部时,您将始终从最后一个数字开始(第一次将1视为最后一个数字)(基本上您保留一个已排序的解决方案)这就是您始终保持唯一解决方案的方式。

#include <iostream>

const int N = 4;

int sol[N];
int sum = 0;
int nr_of_elements;

void back(int lastElement)
{
    if (sum == N)
    {
        for (int i = 0 ; i < nr_of_elements; i++)
            std :: cout << sol[i] << " ";
        std :: cout << "\n";
        return ;
    }
    for ( int i = lastElement ; i <= N - sum ; i++)
    {
        sum += i;
        sol[nr_of_elements++] = i;
        back(i);
        sum -= i;
        nr_of_elements--;
    }
}

int main ()
{
    back(1);
    return 0;
}

答案 1 :(得分:3)

对于每个号码,您只需要检查大于或等于它的号码。例如:

1 1 1 1
1 1 2
1 2 1 (not this, as the third 1 is less than is precedent 2)
1 3
2 1 1 (not this)
2 2
3 1 (not this)
4

答案 2 :(得分:2)

这是Ionescu Roberts的Java版本答案:

static Set<List<Integer>> getNums(int last, int target) {

    Set<List<Integer>> toReturn = new HashSet<List<Integer>>();

    if (target == 0) {
        toReturn.add(new ArrayList<Integer>());
        return toReturn;
    }

    for (int i = last; i <= target; i++) {
        for (List<Integer> subSolution : getNums(i, target - i)) {
            List<Integer> seq = new ArrayList<Integer>(subSolution);
            seq.add(i);
            toReturn.add(seq);
        }
    }

    return toReturn;
}

答案 3 :(得分:2)

传递用作参数的最后一个数字。

void rec(int lastUsed)
{
    for (int i = lastUsed; i <= max; i++)
       rec(i);
}

答案 4 :(得分:0)

解决给定问题的回溯的更清晰实现(参考算法设计手册:此处使用相同的模板)。

    #define V(x)  vector<x >    
    typedef V(int) VI; 

    bool  isSolutionReached(VI & input, VI  & partial, int k,int data)  {
     if (k==data) return true;
         return false; 
    }
    void  processSolution(VI & input,VI & partial, int k,int data)   {
      int sum=0,i=0;    
      for(i=0;i<=data;i++)  
    if(partial[i]!=0 ) {
        sum+=i; 
    }
          if(sum == k)  {
    for(i=0;i<=data;i++) 
                      if(partial[i]!=0) cout <<i<<"\t";
     cout <<"\n"; 
       }
    }

    void  constructNext(VI  & input,VI  & candidateVector,int k,int data) {
        candidateVector.push_back(0);
        candidateVector.push_back(1); 
    }
  bool finished=false; 

    void backTrack(VI & inp, VI  &partial,  int k,int data )  {
   VI  candidateVector; 
   int i=0;
    if( isSolutionReached(partial,inp,k,data))  {
    processSolution(inp,partial,k,data);  
}else {
    k=k+1; 
      constructNext(inp,candidateVector,k,data);  
      for(i=0;i<candidateVector.size();i++)  {
          partial[k]=candidateVector[i];  
          backTrack(inp,partial,k,data); 
      }
}
    }


    int main() { 
  int n=5;  //This is x+1 
  VI inp(5,0);  
  VI partial(5,0); 
  backTrack(inp,partial,0,n-1); 
  return 0; 
   }

答案 5 :(得分:0)

这个答案适用于Java和C#等,你可以覆盖equals和hashcode。

最简单的方法是利用现有算法生成2 ^ N种可能的组合,但调整它以使用Set来消除重复。您的方法将返回一组所有组合,并且不包含重复项。现在您需要告诉Set如何识别2个重复组合:使用列表,并覆盖equals和hashcode方法。

您希望将组合存储在列表中,让我们调用包含java.util.ArrayList的UniqueList:

class UniqueList {
    ArrayList data = new ArrayList();
    void add(Integer item) {
        data.add(item);
    }

    //more wrapper calls if you want

    @Override
    public boolean equals(UniqueList anotherList) {
        if (data.size() != anotherList.data.size()) return false
        //only need to sort once in each list
        Collections.sort(data); 
        Collections.sort(anotherList.data);
        for (int i = 0; i < data.size(); i++) {
            if (data.get(i) != anotherList.data.get(i)) return false
        }

        return true;
    }

    //optionally override hashcode for performance on hashtable
}

现在在现有的生成算法中,使用

Set<UniqueList<Integer>>

保持组合,它将保证没有重复,因为Set将在允许组合之前使用equals()方法进行检查。

您可以添加一个布尔标志来指示列表是否已经排序以避免重复排序 - 换句话说,每个列表只需要排序一次,以便检查重复。

这种方法显然不是最佳方法,但您可以将其插入现有算法中,以最少的代码更改实现0次重复。