[Algorithm Analysis and Design] Backtracking Method (Part 1)


1. Learning points

  Understand the depth-first search strategy of backtracking.
  Master the algorithmic framework for solving problems using the backtracking method
  (1)Recursive backtracking
  (2)Iterative backtracking
  (3)Subset tree algorithm framework
  (4)Permutation tree algorithm framework

  Learn design strategies for backtracking through applied examples.
  (1) Loading problem;
  (2) Batch job scheduling;
  (3) Signed triangle problem
  (4) n-post problem;
  (5) 0-1 knapsack problem;
  (6) Maximum clique problem;
  (7) m coloring of graphs Problem
  (8) Traveling salesman problem
  (9) Circle arrangement problem
  (10) Circuit board arrangement problem
  (11) Continuous postage problem


1.1 Backtracking method

  There are many problems. When it is necessary to find its solution set or when it is required to answer what solution is the best solution that satisfies certain constraints , the backtracking method is often used .
  The basic method of backtracking is search , or an exhaustive search method that is well organized and can avoid unnecessary searches . this methodSuitable for solving some problems with quite large number of combinations. The backtracking method follows the depth-first strategy
  in the solution space tree of the problem .Search the solution space tree starting from the root node. When the algorithm searches to any point in the solution space tree, it first determines whether the node contains the solution to the problem . If it is definitely not included, skip the search for the subtree with the node as the root and backtrack to its ancestor nodes layer by layer ; otherwise, enter the subtree and continue searching according to the depth-first strategy .


1.2 Solution space of the problem

  Solution vector of the problem : The backtracking method hopes that the solution of a problem can be expressed in the form of an n-ary formula (x1, x2,...,xn).
  Explicit constraints : limits on the value of component xi .
  Implicit constraints : Constraints imposed between different components to satisfy the solution of a problem .
  Solution space : For an instance of the problem,All tuples whose solution vectors satisfy explicit constraints constitute a solution space for this instance.

  Note: The same problem can have multiple representations, and some representation methods are simpler and require smaller state spaces (less storage and simpler search methods).
  The solution space of the 0-1 knapsack problem when n=3 is represented by a complete binary tree:
Insert image description here


1.3 Solution space of 0-1 knapsack problem

  The solution space of a problem should contain at least one (optimal) solution to the problem .
  For the 0-1 knapsack problem with n optional items, the solution space consists of 0-1 vectors of length n..
  When n=3, the solution space is {(0,0,0),(0,0,1),(0,1,0),(0,1,1),(1,0,0) ,(1,0,1),(1,1,0),(1,1,1)}
  The solution space is actually the set of solutions !


1.4 Solution space of the traveling salesman problem

  Question: A salesperson wants to go to several cities to sell goods, and the distance (travel expenses) between each city is known. He needs to choose a route that starts from the station, passes through each city, and then returns to the station to minimize the total distance (total travel cost).
Insert image description here
Insert image description here


1.5 Basic method of generating problem status

  White node: A node that has not been visited .
  Gray node: A node that itself has been generated but all its children have not yet been generated is called a gray node .
  Black node: A node where all sons have been generated is called a black node .
  Depth-first problem state generation method: If for an extension node R, once its son C is generated, C will be regarded as a new extension node. After completing the exhaustive search of subtree C (the subtree rooted at C), turn R back into an extension node and continue to generate the next son of R (if it exists).
  Breadth-first problem state generation method: An extended node remains an extended node until it becomes a black node.
  Backtracking: In order to avoid generating problem states where it is impossible to produce the best solution, the bounding function must be continuously used to kill the live nodes that are actually impossible to produce the required solution, so as to reduce the amount of calculation of the problem . The depth-first generation method with bounded functions is called the backtracking method


2. Basic idea of ​​backtracking method

  (1) For the given problem, define the solution space of the problem ;
  (2) Determine the solution space structure that is easy to search ;
  (3) Search the solution space in a depth-first manner, and use pruning functions during the search process to avoid invalid searches .
  Commonly used pruning functions:
  Use the constraint function to prune the subtrees that do not satisfy the constraints at the extended node ;
  use the bounding function to prune the subtrees that cannot obtain the optimal solution .

  A distinctive feature of using the backtracking method to solve problems is that the solution space of the problem is dynamically generated during the search process . At any time, the algorithm only saves the path from the root node to the current expansion node . If the length of the longest path from the root node to the leaf node in the solution space tree is h(n), the computational space required by the backtracking method is usually O(h(n)). Explicitly storing the entire solution space requires O(2h(n)) or O(h(n)!) memory space.


3. Applicable conditions of backtracking algorithm

  P(x1,x2,…,xk) is true at node <x1,x2,…,xk>. That is, if the vector <x1,x2,…,xk> satisfies a certain property, then there is P(x1,x2,…,xk+1)-> P(x1,x2,…,xk) 0<k<n. Call it domino properties .
  ┐ P(x1,x2,…,xk) ->┐ P(x1,x2,…,xk+1) 0<k<n The k-dimensional vector does not meet the constraint conditions, and the expanded
  vector to k+1 dimensions still does not meet the constraints. Only then can you backtrack .


4. Recursive backtracking

  The backtracking method performs a depth-first search on the solution space , therefore,In general, recursive methods are used to implement backtracking methods.

void backtrack (int t)
{
    
    
       if (t>n) output(x);
       else
         for (int i=f(n,t);i<=g(n,t);i++) {
    
    
           x[t]=h(i);
           if (constraint(t)&&bound(t)) backtrack(t+1);
           }
}

5. Iterative backtracking

  useNon-recursive depth-first traversal algorithm for trees, the backtracking method can be expressed as a non-recursive iterative process .

void iterativeBacktrack ()
{
    
    
  int t=1;
  while (t>0) {
    
    
    if (f(n,t)<=g(n,t)) 
      for (int i=f(n,t);i<=g(n,t);i++) {
    
    
        x[t]=h(i);
        if (constraint(t)&&bound(t)) {
    
    
          if (solution(t)) output(x);
          else t++;}
        }
    else t--;
    }
}

6. Subset tree and permutation tree

  When the given problem is to find a subset that satisfies a certain property from a set S of n elements , the corresponding solution space tree is called a subset tree (2n).
  When the given problem is to determine the arrangement of n elements that satisfy a certain property , the corresponding solution space tree is called an arrangement tree (n!).
Insert image description here
  Traversing the subset tree requires O(2n) computation time

void backtrack (int t)
{
    
    
  if (t>n) output(x);
    else
      for (int i=0;i<=1;i++) {
    
    
        x[t]=i;
        if (legal(t)) backtrack(t+1);
      }
}

Insert image description here
  Traversing the permutation tree requires O(n!) computation time

void backtrack (int t)
{
    
    
  if (t>n) output(x);
    else
      for (int i=t;i<=n;i++) {
    
    
        swap(x[t], x[i]);
        if (legal(t)) backtrack(t+1);
        swap(x[t], x[i]);
      }
} 

7. Loading issues

  There is a batch of n containers to be loaded on two ships with load capacities c1 and c2 respectively. The weight of container i is wi, and Insert image description here.
  The loading problem requires determining whether there is a reasonable loading plan that can load this container onto these two ships.. If so, find a loading solution.
  Filling the first ship as much as possible is equivalent to selecting a subset of all containers so that the sum of the container weights in the subset is closest . It can be seen that the loading problem is equivalent to the following special 0-1 knapsack problem.
Insert image description here
  Use the backtracking method to design an O(2n) computational time algorithm for solving the loading problem. This algorithm is superior to dynamic programming algorithms in some cases .

  When n=3, c1=c2=50, and w=[10,40,40]
  if w=[20,40,40]
  Optimal loading plan:
  (1) First fill the first ship as much as possible ;
  (2) Load the remaining containers onto the second ship .

  Solution space: subset tree .
  Feasibility constraint function (select the current element):
  Upper bound function (does not select the current element):
  The current load capacity cw + the weight of the remaining container r ≤ the current optimal load capacity bestw .

void backtrack (int i)
   {
    
    // 搜索第i层结点
      if (i > n)  // 到达叶结点
      更新最优解bestx,bestw;return;
      r -= w[i];
      if (cw + w[i] <= c) {
    
    // 搜索左子树
         x[i] = 1;
         cw += w[i];
         backtrack(i + 1);
         cw -= w[i];      }
      if (cw + r > bestw)  {
    
    
         x[i] = 0;  // 搜索右子树
         backtrack(i + 1);      }
      r += w[i];
   }

Insert image description here


8. Batch job scheduling issues

  n jobs {1, 2, …, n} are to be processed on two machines. Each job must be processed by machine 1 first and then by machine 2. The time required for machine 1 to process job i is ai, and machine 2 The time required to process job i is bi (1≤i≤n). The batch job scheduling problem requires determining the optimal processing order of these n jobs , so that starting from the first job being processed on machine 1, to the last job The end of processing takes the least time on machine 2.
  Obviously,An optimal schedule for batch processing jobs should ensure that machine 1 has no idle time and machine 2 has the smallest idle time.. It can be proved that there is an optimal job schedule such that jobs on machine 1 and machine 2 are completed in the same order .

  Example: There are three jobs {1, 2, 3}. The processing time required for these three jobs on machine 1 is (2, 3, 2), and the processing time required on machine 2 is (1, 1, 3), then the optimal scheduling solutions are (1, 3, 2), (3, 1, 2) and (3, 2, 1), and their completion time is 8.
Insert image description here

Insert image description here
Insert image description here
  Solution space: permutation tree .

void Flowshop::Backtrack(int i)
{
    
    
   if (i > n) {
    
    
       for (int j = 1; j <= n; j++)
         bestx[j] = x[j];
       bestf = f;
       }
   else
      for (int j = i; j <= n; j++) {
    
    
         f1+=M[x[j]][1];
         f2[i]=((f2[i-1]>f1)?f2[i-1]:f1)+M[x[j]][2];
         f+=f2[i];
         if (f < bestf) {
    
    
            Swap(x[i], x[j]);
            Backtrack(i+1);
            Swap(x[i], x[j]);
            }
         f1- =M[x[j]][1];
         f- =f2[i];
         }
}

Insert image description here

class Flowshop {
    
    
   friend Flow(int**, int, int []);
   private:
      void Backtrack(int i);
      int  **M,    // 各作业所需的处理时间
              *x,     // 当前作业调度
        *bestx,    // 当前最优作业调度
             *f2,    // 机器2完成处理时间
              f1,    // 机器1完成处理时间
               f,     // 完成时间和
         bestf,    // 当前最优值
               n;   // 作业数}; 

Guess you like

Origin blog.csdn.net/m0_65748531/article/details/133463462