Common algorithms-backtracking

Source: Internet
Author: User

 

 

The backtracking method is also called the testing method. This method first gives up the limit on the size of the problem temporarily, and enumerates and tests the problem in a certain order. When it is found that the current explain solution cannot be a solution, select the next explain solution. If the current explain solution does not meet all other requirements except the problem scale requirements, continue to expand the scale of the current solution, and continue testing. If the current failover solution meets all the requirements, including the problem scale, this failover solution is a solution to the problem. In the Backtracking Method, the process of dropping the current half solution is called backtracking. Expanding the scale of the current solution is called a forward test.

1. General description of backtracking

The problem P solved by the available backtracking method is usually expressed as: for known records composed of N tuples (x1, x2 ,..., XN) a state space E = {(x1, x2 ,..., XN) ∣ Xi ε Si, I = 1, 2 ,..., N}, given a shard set D for a component of N tuples, all N tuples that require e to satisfy all constraints of D. Where Si is the definition domain of the component XI, and | Si | Limited, I = ,..., N. We call any n tuples in e that satisfy all the constraints of D as a solution of problem P.

The simplest way to solve the problem P is the enumeration method, that is, to check whether all N tuples in e meet all the constraints of d one by one. If yes, it is a solution of problem P. However, it is clear that the computing workload is quite large.

We found that for many problems, the given sequence set D is complete, that is, the I tuples (x1, x2 ,..., Xi) Satisfying D involves only x1, x2 ,..., All constraints of Xi mean that J (j <I) tuples (x1, x2 ,..., XJ) must also meet the requirements that D only involves x1, x2 ,..., All constraints of XJ, I = 1, 2 ,..., N. In other words, as long as there is 0 ≤ j ≤ n-1, make (x1, x2 ,..., XJ) Violation d only involves x1, x2 ,..., One of XJ's constraints is to (x1, x2 ,..., XJ) Any n tuples (x1, x2 ,..., XJ, XJ + 1 ,..., XN) must also violate D, which only involves x1, x2 ,..., A constraint of Xi, n ≥ I> J. Therefore, for the completeness of replica set D p, once a j tuples (x1, x2 ,..., XJ) Violation d only involves x1, x2 ,..., A constraint of XJ, you can be certain, with (x1, x2 ,..., XJ) is any n-tuples (x1, x2 ,..., XJ, XJ + 1 ,..., XN) won't be the solution of the problem P, so you don't have to search for them and detect them. The backtracking method is an algorithm that is more efficient than the enumeration method.

The backtracking method first expresses the state space E of the N tuples of question P as a weighted ordered tree T with N at the height, convert all solutions for problem P in e to all solutions for problem P in T. Tree T is similar to the search tree, which can be constructed as follows:

Let the elements in Si be arranged into XI (1), XI (2 ),..., XI (mi-1), | Si | = mi, I = ,..., N. Starting from the root, let every node in layer I of t have mi sons. This Mi Son is directed to the edge of his parents, in the left-to-right order with the right Xi + 1 (1), XI + 1 (2 ),..., XI + 1 (MI), I = 0, 1, 2 ,..., N-1. According to this constructor, an n tuples in E (x1, x2 ,..., XN) corresponds to a leaf node in T. The weights of n edges from the root of T to the path of the leaf node are x1, x2 ,..., XN, and vice versa. In addition, for any 0 ≤ I ≤ n-1, N tuples in E (x1, x2 ,..., A prefix of xn (x1, x2 ,..., (Xi) corresponds to a non-leaf node in T. The root of T to the path of this non-leaf node, the weights of the I edge are x1, x2 ,..., XI, and vice versa. In particular, the null prefix () of any n tuples in E corresponds to the root of T.

Therefore, finding a problem P in E is equivalent to searching a leaf node in T, N weights x1, x2,… corresponding to n edges from the root of T to the path of the leaf node are required ,..., XN satisfies all constraints of approx. Bundle D. A natural way to search for the required leaf nodes in T is to start from the root and step by step the depth-first strategy, that is, search for the prefix 1 (x1i), prefix 2 (x1, x2 ),..., Prefix I tuples (x1, x2 ,..., XI ),..., Until I = n.

In the Backtracking Method, the tree introduced above is called the state space tree of problem P; any node on the tree T is called the state node of problem P; any leaf node on tree T is called a solution node of problem P; any leaf node on the tree T that satisfies all the constraints of limit set D is called a answer State node of question P, which corresponds to a solution of question p.

[Problem] combination Problem

Problem description: Find the natural numbers 1, 2 ,...... And N.

For example, all combinations of N = 5 and r = 3 are:

(1) 1, 2, 3 (2) 1, 2, 4 (3) 1, 2, 5

(4) 1, 3, 4 (5) 1, 3, 5 (6) 1, 4, 5

(7) 2, 3, 4 (8) 2, 3, 5 (9) 2, 4, 5

(10) 3, 4, 5

The status space for this problem is:

E = {(x1, x2, X3) ∣ Xi ε s, I =, 3} Where: S = {1, 2, 3, 4, 5}

Constraint set: X1 <X2 <X3

Obviously, this replica set is complete.

 

 

2. Method of backtracking

For general problem P with a complete replica set D and its corresponding state space tree T, using the layered structure of T and the completeness of D, the Backtracking Method for searching all solutions of problem P in t can be vividly described:

Starting from the root of T, according to the depth-first policy, the system searches for all the state nodes in the subtree with the root of T, which may contain the answer node, instead, you can skip the search for all Subtrees that certainly do not include the answer node to improve the search efficiency. Specifically, when the search Priority Policy reaches the end point of a State that meets all the constraints in D, the State node is "activated" to continue to the deep search; otherwise, the search for the subtree with the State node as the root is skipped, while tracing back to the ancestor node of the State node layer by layer, while "Killing" the ancestor node whose son node has been searched, the node continues searching until it encounters an ancestor node whose son node has not been searched.

In the search process, as long as the activated status node meets the termination condition, it is the answer node and should be output or saved. Because all solutions to the problem are generally required when the backtracking method is used to solve the problem, tracing is also required after the answer node is obtained to obtain other solutions to the problem, until it is traced back to the root of T and all the son nodes of the root have been searched.

For example, in the combination problem, the tree is first traversed from the root of T. When traversing to a node (1, 2), although it meets the constraints but does not answer the node, it should continue to traverse in depth; when traversing to the leaf knots (1, 2, 5, because it is already an answer node, it stores (or outputs) the node and traces back to its parent node to continue in-depth traversal. When traversing to the node, because it is already a leaf knot, but does not meet the constraints, it also needs to be traced back.

3. General procedures and technologies of backtracking

In the process of solving related problems by using the backtracking method, we usually build a tree while traversing the tree. We generally use non-recursive methods in backtracking. Next, we will give a general process of non-recursive algorithms of the Backtracking Method:

 

When we use the Backtracking Method to Solve the problem, that is, when traversing the state space tree, if we use a non-recursive method, we generally need to use the stack data structure. At this time, not only can the stack be used to represent the node of the tree being traversed, but also can easily represent the process of establishing the child node and backtracking.

For example, in the combination problem, we use a one-dimensional array stack [] to represent the stack. When the stack is empty, it indicates the root node of the tree. If element 1 is added to the stack, the node (1) is established and traversed. If element 2 is added to the stack, the node (1, 2) is established and traversed. element 3 is then added to the stack, indicates that (1, 2, 3) nodes are created and traversed. In this case, it can be determined that it meets all constraints, which is a solution to the problem and output (or saved ). At this time, as long as the top element (3) of the stack goes out of the stack, it indicates that the node (1, 2, 3) is traced back to the node (1, 2 ).

[Problem] combination Problem

Problem description: Find the natural number 1, 2 ,..., N contains all the combinations of r numbers.

Use the Backtracking Method to locate the problem and save the found combination in ascending order of a [0], a [1],…, In a [r-1], the elements of the combination meet the following properties:

(1) A [I + 1]> A. the last digit is greater than the previous digit;

(2) A-I <= N-R + 1.

The process of searching for solutions can be described as follows:

First, discard the condition that the number of combinations is R. The candidate combination starts with only one number 1. Because the solution satisfies all the conditions except the problem scale, expands the scale and satisfies the above conditions (1), the candidate group is changed to 1, 2. Continue this process and obtain the candidate combinations 1, 2, and 3. This solution satisfies all the conditions, including the problem scale, and thus is a solution. On the basis of this solution, select the next partial solution. Because 3 on a [2] is adjusted to 4, and 5 is adjusted to meet all the requirements of the problem, solution 1, 2, and, 4, 1, 2, 5. Since 5 cannot be adjusted any more, we need to go back from a [2] to a [1]. At this time, a [1] = 2 can be adjusted to 3 and try again, solution 1, 3, and 4 are obtained. Repeat the previous test and Backward Tracing until a [0] is used for backtracking, which indicates that all solutions to the problem have been found. Write the program as follows:

[Program]

# Define maxn100

Int A [maxn];

Void comb (int m, int R)

{Int I, J;

I = 0;

A = 1;

Do {

If (a-I <= m-R + 1

{If (I = R-1)

{For (j = 0; j <r; j ++)

Printf ("% 4D", a [J]);

Printf ("/N ");

}

A ++;

Continue;

}

Else

{If (I = 0)

Return;

A [-- I] ++;

}

} While (1)

}

 

Main ()

{Comb (5, 3 );

}

[Problem] crossword game

Problem description: Fill in 9 numbers in the square matrix of 3 × 3 squares ranging from 1 to n (n ≥ 10). Fill in an integer for each square, returns the sum of two integers in two adjacent squares. Try to find all the numeric filling methods that meet this requirement.

You can use the test to find a solution to the problem, that is, starting from the first square, to find a reasonable integer for the current square, and fill in the correct position, find the appropriate integer that can be filled in for the next square. If you cannot find a reasonable certificate that can be filled in for the front square, you must roll back to the front square and adjust the number of filled items in the front square. When the ninth square is also filled with a reasonable integer, a solution is found, the solution is output, and the ninth filled integer is adjusted to find the next solution.

To find a nine-digit filling method that meets the requirements, fill in an integer at the current position each time starting from not filling in a certain order (for example, in the ascending order, then, check whether the currently entered integer meets the requirements. When the requirements are met, fill in the next square with an integer in the same way. If the recently entered integer does not meet the requirements, change the entered integer. If all possible integers in the current square cannot meet the requirements, you have to roll back to the previous square and adjust the integer entered in the previous square. Execute the extension, check or adjustment and check repeatedly until a solution meeting the problem requirements is found and the solution is output.

The backtracking method is used to find an algorithm for the solution:

{Int m = 0, OK = 1;

Int n = 8;

Do {

If (OK) extension;

Else adjustment;

OK = check the rationality of filling in the first M integers;

} While ((! OK | M! = N) & (M! = 0 ))

If (M! = 0) Output solution;

Else outputs a non-solution report;

}

If the program is looking for all solutions, after outputting the found solutions, it should continue to adjust the integers filled in at the last position and try to find the next solution. The algorithm is as follows:

Algorithms used to find all solutions:

{Int m = 0, OK = 1;

Int n = 8;

Do {

If (OK)

{If (M = N)

{Output solution;

Adjustment;

}

Else extension;

}

Else adjustment;

OK = check the rationality of filling in the first M integers;

} While (M! = 0 );

}

To ensure that the program can be terminated, you must ensure that the number-filling sequence that has been abandoned will not be re-tested during the adjustment, that is, you must generate a number-filling sequence based on a certain model. Set a tested sequence for the solution candidates, and form and test candidates one by one. From small to large or from large to small, can be used. For example, you must first enter integer 1 in the new position during expansion. When you adjust the value, find the next unused integer in the current explain solution. Compile the above extension, adjustment, and validation into a program. For details, refer to the program for all solutions below.

[Program]

# Include <stdio. h>

# Define N 12

Void write (int A [])

{Int I, J;

For (I = 0; I <3; I ++)

{For (j = 0; j <3; j ++)

Printf ("% 3d", a [3 * I + J]);

Printf ("/N ");

}

Scanf ("% * C ");

}

 

Int B [n + 1];

Int A [10];

Int isprime (INT m)

{Int I;

Int primes [] = {2, 3, 5, 7, 11, 17, 19,23, 29,-1 };

If (M = 1 | M % 2 = 0) return 0;

For (I = 0; primes> 0; I ++)

If (M = primes) return 1;

For (I = 3; I * I <= m ;)

{If (M % I = 0) return 0;

I + = 2;

}

Return 1;

}

 

Int checkmatrix [] [3] = {-1}, {0,-1}, {1,-1}, {0,-1}, {1, 3, -1 },

{2, 4,-1}, {3,-1}, {4, 6,-1}, {5, 7,-1 }};

Int selectnum (INT start)

{Int J;

For (j = start; j <= N; j ++)

If (B [J]) return J

Return 0;

}

 

Int check (int pos)

{Int I, J;

If (Pos <0) return 0;

For (I = 0; (j = checkmatrix [POS])> = 0; I ++)

If (! Isprime (A [POS] + A [J])

Return 0;

Return 1;

}

 

Int extend (int pos)

{A [++ POS] = selectnum (1 );

B [a] [POS] = 0;

Return Pos;

}

 

Int change (int pos)

{Int J;

While (Pos> = 0 & (j = selectnum (A [POS] + 1) = 0)

B [A [pos --] = 1;

If (Pos <0) Return-1

B [A [POS] = 1;

A [POS] = J;

B [J] = 0;

Return Pos;

}

 

Void find ()

{Int OK = 0, Pos = 0;

A [POS] = 1;

B [A [POS] = 0;

Do {

If (OK)

If (Pos = 8)

{Write ();

Pos = change (POS );

}

Else Pos = extend (POS );

Else Pos = change (POS );

OK = check (POS );

} While (Pos> = 0)

}

 

Void main ()

{Int I;

For (I = 1; I <= N; I ++)

B = 1;

Find ();

}

[Problem] n queen's question

Problem description: Find all the la s of N x n chess boards that cannot be captured by each other.

This is a question from chess. The Queen can capture each other in four directions: vertical and horizontal lines and two diagonal lines ., If a queen is placed in the position of 4th rows and 3rd columns on the board, any queen playing the "X" position on the board will be able to capture each other with the queen.

 

1 2 3 4 5 6 7 8

××

×××

×××

××Q ×××××

×××

×××

××

××

We can get the following inspiration: a proper solution should be that there is only one queen on each column and each row, and there is only one queen on one diagonal line.

The solution starts with an empty configuration. On the basis of the rational configuration of column 1st to column M, configure column M + 1 until the configuration of column N is reasonable, a solution is found. Next, change the configuration of column N to obtain the next solution. In addition, there may be n configurations in any column. At the beginning, the configuration is set to 1st rows. When it is changed later, select 2nd rows, 3rd rows ,... , Until the nth line. When the n-th row does not find a reasonable configuration, You need to trace back to change the configuration of the previous column. The algorithm for solving the Queen's problem is as follows:

{Enter the chessboard size value N;

M = 0;

Good = 1;

Do {

If (good)

If (M = N)

{Output solution;

Changes to form the next solution;

}

Else extends the current candidate to the next column;

Else changes to form the next explain solution;

Good = check the rationality of the current explain solution;

} While (M! = 0 );

}

Before writing a program, determine the data structure of the edges board. The intuitive method is to use a two-dimensional array, but you will find that this representation method makes it difficult to adjust the solution and check its rationality. A better way is to express frequently-used information as directly as possible. For this question, "Common Information" is not the specific location of the Queen, but "whether a queen has been properly placed with a diagonal line ". Because a queen is placed on a column, a one-dimensional array (COL []) is introduced. The value Col indicates that there is a queen in column I and column Col on the board. For example, Col [3] = 4 indicates that there is a queen on the 3rd columns and 4th rows of the Board. In addition, in order to make the program trace back to the initial position after finding the full solution, set the initial value of Col [0] to 0. When we trace back to column 0th, it means that the program has obtained all solutions, end the program running.

To make it easy and convenient for the program to check the configuration rationality of the queen, the following three work arrays are introduced:

(1) array a [], a [k] indicates that there is no queen on row K;

(2) array B [], B [k] indicates no queen on the Right high left low diagonal line of column K;

(3) array C [], C [k] indicates no queen on the left high and right low diagonal lines of column K;

The square on the same right-high left-low diagonal line in the checker, and the sum of their row numbers and column numbers is the same; the square on the same left-high right-low diagonal line, their row numbers are the same as the column numbers.

Initially, there was no queen on all rows and diagonal lines, starting with configuring the first queen in the second row of column 1st, when a reasonable queen is placed in column M Col [m, in arrays A [], B [], and C [] are column M, and the position of row Col [m] is set with the Queen mark; when you go back from column M to M-1 are ready to adjust the Queen configuration of column M, clear the Queen's flag for M-1 in arrays A [], B [], and C. The configuration of a queen in the column M is reasonable in the cell of Col [M]. The values of the positions of arrays A [], B [], and C [] are both 1. For details, see the following procedure:

[Program]

# Include <stdio. h>

# Include <stdlib. h>

# Define maxn 20

Int n, m, good;

Int Col [maxn + 1], a [maxn + 1], B [2 * maxn + 1], C [2 * maxn + 1];

 

Void main ()

{Int J;

Char awn;

Printf ("enter N:"); scanf ("% d", & N );

For (j = 0; j <= N; j ++) A [J] = 1;

For (j = 0; j <= 2 * n; j ++) cb [J] = C [J] = 1;

M = 1; Col [1] = 1; good = 1; Col [0] = 0;

Do {

If (good)

If (M = N)

{Printf ("column/T row ");

For (j = 1; j <= N; j ++)

Printf ("% 3d/T % d/N", J, Col [J]);

Printf ("enter a character (Q/Q for exit )! /N ");

Scanf ("% C", & awn );

If (AWN = 'q' | awn = 'q') Exit (0 );

While (COL [m] = N)

{M --;

A [col [m] = B [M + Col [m] = C [n + M-Col [m] = 1;

}

Col [m] ++;

}

Else

{A [col [m] = B [M + Col [m] = C [n + M-Col [m] = 0;

Col [++ m] = 1;

}

Else

{While (COL [m] = N)

{M --;

A [col [m] = B [M + Col [m] = C [n + M-Col [m] = 1;

}

Col [m] ++;

}

Good = A [col [m] & B [M + Col [m] & C [n + M-Col [m];

} While (M! = 0 );

}

The search algorithm of the test method is often written as a recursive function. The functions queen_all () and queen_one () in the following two programs can be used to solve all the solutions of the Queen problem and one solution respectively.

[Program]

# Include <stdio. h>

# Include <stdlib. h>

# Define maxn 20

Int N;

Int Col [maxn + 1], a [maxn + 1], B [2 * maxn + 1], C [2 * maxn + 1];

Void main ()

{Int J;

Printf ("enter N:"); scanf ("% d", & N );

For (j = 0; j <= N; j ++) A [J] = 1;

For (j = 0; j <= 2 * n; j ++) cb [J] = C [J] = 1;

Queen_all (1, N );

}

 

Void queen_all (int K, int N)

{Int I, J;

Char awn;

For (I = 1; I <= N; I ++)

If (A & B [K + I] & C [n + k-I])

{Col [k] = I;

A = B [K + I] = C [n + k-I] = 0;

If (k = N)

{Printf ("column/T row ");

For (j = 1; j <= N; j ++)

Printf ("% 3d/T % d/N", J, Col [J]);

Printf ("enter a character (Q/Q for exit )! /N ");

Scanf ("% C", & awn );

If (AWN = 'q' | awn = 'q') Exit (0 );

}

Queen_all (k + 1, n );

A = B [K + I] = C [n + k-I];

}

}

Finding a solution using a recursive method is slightly different from finding all the solutions. In the algorithm for finding a solution, the recursive algorithm must answer the question about whether the current solution can eventually become a solution. When it becomes the final solution, the recursive function no longer performs recursive testing and returns immediately. If it cannot be a solution, it must continue testing. If the queen_one () function returns 1, the solution is found, and 0 indicates that the current explain solution cannot be the solution. For details, see the following functions.

[Program]

# Define maxn 20

Int N;

Int Col [maxn + 1], a [maxn + 1], B [2 * maxn + 1], C [2 * maxn + 1];

Int queen_one (int K, int N)

{Int I, found;

I = found = 0;

While (! Found & I <n)

{I ++;

If (A & B [K + I] & C [n + k-I])

{Col [k] = I;

A = B [K + I] = C [n + k-I] = 0;

If (k = N) return 1;

Else

Found = queen_one (k + 1, n );

A = B [K + I] = C [n + k-I] = 1;

}

}

Return found;

}

 

Contact Us

The content source of this page is from Internet, which doesn't represent Alibaba Cloud's opinion; products and services mentioned on that page don't have any relationship with Alibaba Cloud. If the content of the page makes you feel confusing, please write us an email, we will handle the problem within 5 days after receiving your email.

If you find any instances of plagiarism from the community, please send an email to: info-contact@alibabacloud.com and provide relevant evidence. A staff member will contact you within 5 working days.

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.