Although the arrangement is often encountered in the life of the problem, can be in the program design, not in-depth thinking or lack of experience to let people start. Because the permutation combination problem always first takes the combination to arrange, and the simple arrangement question is relatively simple, therefore this article only carries on the detailed discussion to the combinatorial question realization. Take the number of M (0<m<=n) in the number of n as an example, the problem can be decomposed into:
1. First, select the largest number from the number of N, and then in the remaining number of N-1 select the number of m-1 until the number of N (m-1) selected from 1.
2. From the number of N to select a number of small numbers, continue to perform 1 steps until the current optional number of the largest number of M.
Obviously, the above method is a recursive process, that is, the recursive method can be very clean and neat to obtain all the combinations.
The following is the implementation of the Recursive method:
Copy Code code as follows:
Find all combinations of M elements from the array A[1..N].
A[1..N] Represents the candidate set, N is the candidate set size, n>=m>0.
B[1..M] is used to store elements in the current group (where the element subscript is stored),
Constant M represents the number of elements in a combination that satisfies a condition, m=m, which are used only to output the results.
void combine (int a[], int n, int m, int b[], const int m)
{
for (int i=n; i>=m; i--)//Note the circular range here
{
B[m-1] = i-1;
if (M > 1)
Combine (a,i-1,m-1,b,m);
else//M = = 1, output a combination
{
for (int j=m-1; j>=0; j--)
cout << A[b[j]] << "";
cout << Endl;
}
}
}
Because the recursive program can be introduced into the stack, with backtracking into the corresponding non-recursive procedures, so the combination of problems can be solved by backtracking method. For the sake of understanding, we can classify the combinatorial problem as the path traversal problem of graph, select all the combinations of M in the number of N, equivalent to one such diagram (illustrated by the example of 3 numbers from 1,2,3,4) to [m,x] from [1,1] (m<=x<=n All paths to the location:
Copy Code code as follows:
The above image is the first m row of the upper-right diagonal matrix of the intercept nxn, if each element in the moment is treated as a node in the graph, all the combinations we require are equivalent to all the paths from the first column of the first row [1,1], to any column of the third row, to the end, and to the nodes between the adjacent rows. And the next line of nodes must be on the right side of the last line to be connected to the path, other cases are not connected. Clearly, the sequence of numbers that passes through a path corresponds to a combination of requirements.
The following is the implementation of the non-recursive backtracking method:
Copy Code code as follows:
Find all combinations of M elements from the array A[1..N].
A[1..N] Represents a candidate set, and M represents the number of elements in a combination.
Returns the total number of all combinations.
int combine (int a[], int n, int m)
{
m = m > n? n:m;
int* order = new int[m+1];
for (int i=0; i<=m; i++)
Order[i] = i-1; Note Here order[0]=-1 is used as a loop to determine the identity
int count = 0;
int k = m;
BOOL flag = TRUE; Flag to find a valid combination
while (order[0] = =-1)
{
if (flag)//output meets the required combination
{
for (I=1; i<=m; i++)
cout << A[order[i]] << "";
cout << Endl;
count++;
Flag = false;
}
order[k]++; Select a new number at the current location
if (order[k] = n)//Current position has no number to choose from, backtracking
{
order[k--] = 0;
Continue
}
if (K < m)//update the number at the next position in the current position
{
ORDER[++K] = order[k-1];
Continue
}
if (k = = m)
Flag = true;
}
Delete[] Order;
return count;
}
Here is the program that tests the above functions:
Copy Code code as follows:
int main ()
{
const int N = 4;
const int M = 3;
int a[n];
for (int i=0;i<n;i++)
A[i] = i+1;
Backtracking method
cout << Combine (a,n,3) << Endl;
Recursive method
int b[m];
Combine (a,n,m,b,m);
return 0;
}
From the above analysis, it is known that the general algorithm for solving combinatorial problems is recursive and backtracking two kinds. When it comes to specific problems, recursion is not a good choice for large combinatorial problems because of the limitation of recursion in recursive layers, in which case only backtracking can be used.
The whole permutation problem of n number is relatively simple, which can be realized by ordinal enumeration in the exchange position. STL provides an algorithm next_permutation to find the next permutation of a sequence, and its algorithm principle is as follows:
1. Starting at the end of the current sequence to look forward to two adjacent elements, so that the previous element is *i, the latter element is *ii, and meet *i<*ii;
2. Start the scan again from the current sequence end to find the first element larger than *i, *j (J may Equal II), and swap the I,J elements;
3. Reverses the order of all elements after II (including II) so that the resulting arrangement is the next permutation of the current sequence.
The implementation code is as follows:
Copy Code code as follows:
Template <class bidirectionaliterator>
BOOL Next_permutation (Bidirectionaliterator, Bidirectionaliterator last)
{
if (i = last) return false; Air Range
Bidirectionaliterator i = A;
++i;
if (i = = last) return false; Only one element
i = last; I point to the tail end
I.;
for (;;)
{
Bidirectionaliterator II = i;
I.;
Above, lock a group (two) adjacent elements
if (*i < *II)//If the previous element is less than the last element
{
Bidirectionaliterator j = Last; Make J point to end
while (!) ( *i < *--j)); Look forward from the end until you encounter a larger element than *i.
Iter_swap (i, j); Exchange I, J
Reverse (ii, last); To rearrange all the elements after II
return true;
}
if (i = = first)//go to the front
{
Reverse (the last); All reverse rearrangement
return false;
}
}
}
The following program demonstrates the use of next_permutation to find a sequence full arrangement:
Copy Code code as follows:
int main ()
{
int ia[] = {1,2,3,4};
Vector<int> IV (IA,IA+SIZEOF (IA)/sizeof (int));
Copy (Iv.begin (), Iv.end (),ostream_iterator<int> (cout, ""));
cout << Endl;
while (Next_permutation (Iv.begin (), Iv.end ()))
{
Copy (Iv.begin (), Iv.end (),ostream_iterator<int> (cout, ""));
cout << Endl;
}
return 0;
}
Note: The original sequence in the above program is in the order of the number of small to large, if the initial sequence is disorderly, the above program can only find from the current sequence to start the following part of the arrangement, that is, next_permutation to find the arrangement is arranged in order from small to large orders.