Algorithm backtracking method

Keywords: Algorithm

Backtracking method

1. General

Backtracking is an optimization search method, which searches forward according to the optimization conditions to achieve the goal. However, when a certain step is explored and it is found that the original selection is not excellent or fails to achieve the goal, it will go back to one step and reselect. This technology of going back and going again if it fails is the backtracking method, and the point in a certain state that meets the backtracking conditions is called the "backtracking point".

Backtracking is a systematic and jumping search algorithm. In the solution space tree containing all solutions of the problem, it searches the solution space tree from the root node according to the depth first strategy. When the algorithm searches any node of the solution space tree, it always judges whether the node does not contain the solution of the problem. If it is definitely not included, skip the system search of the subtree with this node as the root, and trace back to its ancestor node layer by layer. Otherwise, enter the subtree and continue to search according to the depth first strategy. When the backtracking method is used to find all solutions of the problem, it needs to backtrack to the root, and all subtrees of the root node have been searched. When the backtracking method is used to find any solution of the problem, it can end as long as a solution of the problem is searched. This algorithm, which systematically searches the solution of the problem in a depth first way, is called backtracking method. It is suitable for solving some problems with a large number of combinations

Applicable conditions

The solution of the problem is represented by a vector X = (x1, x2,..., xn)
It is necessary to search the optimal solution of one or a group of solutions that meet the constraints

2. Three concepts

**Constraint function: * * constraint function is determined according to the meaning of the question. By describing the general characteristics of the legal solution, it is used to remove the illegal solution, so as to avoid continuing to search the rest of the illegal solution. Therefore, the constraint function is effective and equivalent to any node in the state space tree.
**State space tree: * * the solution of a problem can be expressed as solution vector X = (x1, x2,..., xn). The combination of all values of each component xi in X constitutes the solution vector space of the problem, which is referred to as solution space or solution space tree, also known as state space tree. It is a graphical description of all solutions. The solution of each child node in the tree is only one part different from the parent node.
**Extension node, live node, dead node: * * the so-called extension node is the node that is currently finding its child nodes. In DFS, only one extension node is allowed. A live node is a node that meets the requirements of the constraint function by comparing it with the constraint function; Dead node. Therefore, it is easy to know that the dead node does not have to find its child nodes (meaningless).
Because there is a process of returning to the ancestor node when using the backtracking method, the searched nodes need to be saved. Usually: define stack to save, and adopt recursive method
Backtracking usually adopts two strategies (both called pruning function) to avoid invalid search. The constraint function is used to cut the path that does not meet the constraint conditions at the extension node
Cut out the path that can not get the solution or optimal solution of the problem with the bound function

3. Problem solving steps

(1) Describe the form of solution and define a solution space, which contains all solutions of the problem. This step mainly defines the solution space tree of the problem.
(2) Determine the extended search rules of the node.
(3) Construct constraint functions (used to kill nodes). Then it is necessary to complete backtracking through DFS thought. DFS can adopt recursive backtracking or non recursive (iterative) backtracking. Determine the solution space of the problem -- >
Determine the extended search rules of nodes – > search the solution space in DFS mode, and use pruning function to avoid invalid search in the search process.

1: Symbolic triangle problem

1. Problem description

Symbolic triangle problem: the following are "-". The following figure is a symbolic triangle composed of 14 "+" and 14 "-". There are "+" under the two same signs and "-" under the two different signs.

In general, the first line of the symbol triangle has n symbols. The symbolic triangle problem requires that for a given n, how many different symbolic triangles are calculated to make the number of "+" and "-" contained the same.
The reference code is as follows. On this basis, please realize the following functions:

  1. When the value of n is 1-20, the number of corresponding symbol triangles is output. If there is no symbol triangle that meets the conditions, 0 is output
  2. Enter an integer n, output the number of corresponding symbolic triangles, and display all symbolic triangles in turn.

2. Problem solving ideas

(1) Problem solving ideas:
1. By constantly changing each symbol in the first line and searching for a qualified solution, recursive backtracking can be used.
For ease of operation, set + to 0 and - to 1, so that the XOR operator can be used to represent the relationship of symbolic triangles:
++Is + that is 0 ^ 0 = 0, -- is + that is 1 ^ 1 = 0, ± is - that is 0 ^ 1 = 1, - + is - that is 1 ^ 0 = 1
2. Because the two symbols have the same number, you can prune the problem solving tree
There is no solution when the total number of all symbols is odd, and there is no solution when a symbol exceeds half the total number
(2) Algorithm design
After the first i symbols x[1:i] in the first row of the symbol triangle are determined, a symbol triangle composed of i*(i+1)/2 symbols is determined.
Next, after the value of x[1:i] is determined, as long as an edge is added to the right of the previously determined symbol triangle, it can be extended to the symbol triangle corresponding to x[1:i].
In the process of backtracking search, the number of "+" signs and "-" signs contained in the current symbolic triangle can not exceed n*(n-1)/4 as feasibility constraints to cut out subtrees that do not meet the constraints.

3. Program code

public  class Triangles{
 static int n, //Number of symbols in the first line
 count, //n*(n+1)/2
 half; //Number of current "+"
 static int [][] p; //Signed triangle matrix
 static long sum; // Number of symbolic triangles found
 
 public static long compute(int nn)
{
      n=nn;
      count=0;
      sum=0;
      half=n*(n+1)/2;
      if (half%2==1) return 0;
      half=half/2;
      p=new int[n+1][n+1];
      for(int i=0;i<=n;i++)	
	 for(int j=0; j<=n;j++)
	   p[i][j]=0;
      backtrack(1);
      return sum;
}

 public static void backtrack (int t)
{   
   if((count>half)||(t*(t-1)/2-count>half))
   {   
       return;  //Cut out subtrees that do not satisfy constraints
   }     
   if(t>n)
    {   
    	sum++; //Find a triangle that meets the requirements
        /*
           You can add code here to output the symbol triangle corresponding to the current solution 
*/
    } 
   else
	for(int i=0;i<2;i++)
	{//subtree
	   p[1][t]=i;
	   count+=i;
	   for(int j=2;j<=t;j++) //The triangle formed by the subtree
	   {
	      p[j][t-j+1]=p[j-1][t-j+1]^p[j-1][t-j+2];
	      count+=p[j][t-j+1];
	   }		   
           backtrack(t+1);
           for(int j=2;j<=t;j++) //Backtracking recovery
	     count-=p[j][t-j+1];
	   count-=i;
   }
}  

 public static void main(String[] args) {
     int n=4; long result;
     result=compute(n);
     System.out.println(String.format("result=%d",result));
  }  
}

package suanfa;
public class suanfa1 {
 
	static int n;//Number of symbols in the first line
	static int half;//n*(n+1)/4
	static int count;//Number of current "+" or "-"
	static int[][] p;//Signed triangle matrix
	static long sum;//Number of symbolic triangles found
	
	public static float Compute(int nn) {
		n = nn;
		count = 0;
		sum = 0;
		half = (n*(n+1))>>1;
		if((half>>1)<<1 != half) {
			return 0;
		}
		half = half>>1;
		p = new int[n+1][n+1];
		backtrack(1);
		
		return sum;
	}
	
	/**
	 * Algorithm 1
	 * @param t
	 */
	
	public static void backtrack01(int t) {
		if(count>half || (t*(t-1)/2-count > half)) {//Pruning the problem tree
			return;
		}
		if(t>n) {
			sum++;//Total number of signed triangles + 1
		}
		else {
			//There are two cases for each location 0,1
			for(int i = 0;i<2;i++) {
				p[1][t] = i;
				count += i;//The number of "-" is technically for pruning
				
				//Next, draw the remaining n-1 lines
				for(int j = 2;j<=t;j++) {
					//Find the placement method of the remaining rows by XOR
					p[j][t-j+1] = p[j-1][t-j+1]^p[j-1][t-j+2];
					count += p[j][t-j+1];	
				}
				backtrack01(t+1);
				
				//Restore site
				for(int j = 2;j<=t;j++) {
					count -= p[j][t-j+1];
				}
				count -= i;
			}
		}
	}
	
	
	public static void backtrack(int t) {
		if((count>half)||((t*(t-1)/2-count > half)) ){//Pruning the problem tree
			return;
		}
		if(t>n) {
			sum++;
			//Print symbol triangles
			for(int i =1;i<=n;i++) {
				for(int k = 1;k<i;k++) {
					System.out.print(" ");
				}
				for(int j =1;j<=n;j++) {
					if(p[i][j] == 0 && j<=n-i+1) {
						System.out.print("+" + " ");
					}
					else if(p[i][j] == 1 && j<=n-i+1) {
						System.out.print("-" + " ");
					}
					else {
						System.out.print("  ");
					}
				}
				System.out.println();
			}
			System.out.println();
		}
		else {
			//There are two cases for each location 0,1
			for(int i =0;i<2;i++) {
				p[1][t] = i;
				count += i;//Calculate the number of "-"
				
				//Next, draw the remaining n-1 lines
				for(int j = 2;j<=t;j++) {
					//Find the placement method of the remaining rows by XOR
					p[j][t-j+1] = p[j-1][t-j+1]^p[j-1][t-j+2];
					count += p[j][t-j+1];
					
				}
				backtrack(t+1);
				
				//Restore site
				for(int j =2;j<=t;j++) {
					count -= p[j][t-j+1];
				}
				count -= i;
				
			}
		}
	}
 
	
	public static void main(String[] args) {
		// TODO Auto-generated method stub
		float SUM = Compute(4);	
		System.out.print("total: " + SUM);
	}
 
}

2: Integer transformation problem

1. Problem description

The two transformations of integer i are defined as, (rounding down); An algorithm is designed to find the given two integers a and b, and the integer a is transformed into b with the least number of sum transformations; for example
Implementation tips:
Observing the two operations of F and G, we can see that f always makes i larger and G always makes i smaller. Therefore, the size relationship between i and the target value m can be determined before deciding which operation to let x perform. If x > m, let it perform g operation; Otherwise, perform the f operation.
The solution of the problem can be divided into two cases. One is that there is a solution, that is, n can be transformed into m by function; The other is no solution, that is, n cannot be transformed into m by function.
It's easy to have a solution. Just judge whether the last i is equal to M. If i is equal to m, then n has been transformed into m and returned recursively.
If there is no solution, the following example can be used for analysis. Suppose input n=9,m=5.
n> M, execute g,n=[9/2]=4
N < m, execute f,n=34=12
n> M, execute g,n=[12/2]=6
n> M, execute f,n=[6/2]=3
N < m, execute g,n=33=9
n> M, execute f,n=[9/2]=4
If the value of N falls into a repeated loop, and if the previously calculated elements appear in the process of recursion, it means that n cannot be converted to m. The implementation of this method is slightly complex, and it is necessary to judge whether the current calculated value has appeared before. Another simple processing method: for the case that m cannot change into N in any way, a judgment condition can be added, for example, until the depth reaches a large value (such as 1000).
The backtracking method is realized by subset tree, and the subset tree structure is as follows:

There are two backtracking return conditions, one is that i is equal to m, and the other is that there are duplicate numbers. The second return condition can be determined by a function test.
Pruning conditions:
Explicit constraint: if x > m, cut off its left subtree; If x < m, cut off its right subtree;
Implicit constraint: if it is found that the current calculation times are greater than or equal to the minimum calculation times during a calculation, this branch is cut off.

2. Program code

package suanfa;
import java.util.Scanner;
public class suanfa1 {
    static int m,n1; //n1 facilitate the following output form
    static int tempcount,bestcount;//Current transformation times, optimal transformation times
    static int[] temp1=new int[100];
    static int[] temp2=new int[1000];
    public static void main(String args[]){
        Scanner input=new Scanner(System.in);
        System.out.println("Please enter an integer to transform");
        int n=input.nextInt();
        n1=n;
        System.out.println("Please enter the integer to be transformed");
        m=input.nextInt();
        tempcount=0;
        bestcount=100;
        test(n);
    }

    //Implementation method
    public static void test(int n){
        if(tempcount>99){  //The best transformation times have been greater than a certain value, which is the case of no solution (implicit constraint)
            System.out.println("unsolvable");
        }
        else{
            if(n==m){          //Solvable case
                if(tempcount<bestcount)     //
                    bestcount=tempcount;
                System.out.println("Optimal transformation times:"+bestcount);
                System.out.printf("%d=",m);
                for(int i=bestcount;i>=1;i--){
                    if(temp2[i]==2)
                        System.out.print("f");
                    if(temp2[i]==1)
                        System.out.print("g");
                }
                System.out.printf("(%d)",n1);
            }
            else if(n>m){   //n> M take this branch to prune
                n=g(n);
                tempcount++;
                temp2[tempcount]=1;
                test(n);
            }
            else {      //N < m take this branch
                n=f(n);
                tempcount++;
                temp2[tempcount]=2;
                test(n);
            }
        }
    }
    //f(i)=3*i
    public static int f(int n){
        n=3*n;
        return n;
    }
    //g(i)=i/2
    public static int g(int n){
        n=n/2;
        return n;
    }
}

3: Subsets and problems

1. Problem description

Given set s, there are n positive integers in s, and M is a positive integer. The subset sum problem determines whether there is a subset S1 of s so that the sum of the elements in S1 is equal to M. Please design a backtracking method to solve subsets and problems. If the problem has No Solution, output "No Solution". If the problem has a solution, output the values that meet the elements in subset S1.

2. Program code

package suanfa;
import java.util.Scanner;
public class suanfa2 {
    public static void main(String[] args) {
        SubSet subSet=new SubSet();
        Scanner scanner = new Scanner(System.in);
        System.out.println("Enter the length and the sum of the subsets you want");
        int len,m;
        len=scanner.nextInt();
        m=scanner.nextInt();
        int[] nums=new int[len];//Initial array
        int[] sets=new int[len];//Subset array
        System.out.println("Input initial array");
        for(int i=0;i<len;i++)
            nums[i]=scanner.nextInt();
        subSet.setNums(nums);
        subSet.setSets(sets);
        subSet.setTotal(m);
        subSet.setCount(0);
        subSet.setErrStr("No Solution");
        subSet.backTrack(0);
        if(subSet.getCount()==0){
            System.out.println(subSet.getErrStr());
        }
    }
}

subSet.class

package suanfa;
import java.util.Arrays;
public class SubSet{
    private int[] nums;//Initial array
    private int[] sets;//Subset array
    private int total;//and
    private int count;//Number of existing subsets
    private String errStr;//The statement entered when there is no subset

    public void setErrStr(String errStr) {
        this.errStr = errStr;
    }
    public void setNums(int[] nums) {
        this.nums = nums;
    }
    public void setSets(int[] sets) {
        this.sets = sets;
    }
    public void setTotal(int total) {
        this.total = total;
    }
    public void setCount(int count) {
        this.count = count;
    }
    public int[] getNums() {
        return nums;
    }
    public int[] getSets() {
        return sets;
    }
    public int getTotal() {
        return total;
    }
    public int getCount() {
        return count;
    }
    public String getErrStr() {
        return errStr;
    }
    public void backTrack(int idx){
        if(total==0){
            System.out.println(Arrays.toString(sets));
            count++;
            return;
        }
        if(idx==nums.length||total<0)
            return;
        else{
            for(int i=idx;i<nums.length;i++){
                if(nums[i]<=total){
                    sets[i]=nums[i];
                    total-=nums[i];
                    backTrack(i+1);
                    sets[i]=0;
                    total+=nums[i];
                }
            }
        }
    }
}

Test data 1:
Input:

4  31
13 24 11 7

Output:

[13, 0, 11 , 7]
[0, 24, 0 , 7]

Test data 2:
Input:

5  235
123 55 9 4 11

Output:

No Solution

4: Assignment problem

1. Problem description

There are n jobs assigned to n individuals. The labor fee to be paid for assigning work i to the j-th person is cij. Please design an algorithm to assign each person a different job and minimize the total labor fee.
Implementation tips: the solution space of this problem is an permutation tree, which can be realized by the backtracking framework of search permutation tree.
Input format:
The first line of the input data is a positive integer n (1 ≤ n ≤ 20), indicating the quantity of work, and then enter n lines, n numbers per line, indicating the corresponding labor cost.
Input example:
3
10 2 3
2 3 4
3 4 5
Output example:
9

2. Program code

package suanfa;
import java.util.Scanner;
public class suanfa3 {
	public static int sum = 0,n;
	public static int [] [] num ;
	public static boolean []  bool;
	public static int min = Integer.MAX_VALUE;
	public static void main(String[] args) {
		Scanner sc =new Scanner(System.in);
		 n = sc.nextInt();
		num = new int [n+1][n+1];
		bool = new boolean [n+1];
		for (int i = 1; i <=n; i++) {
			for (int j = 1; j <=n; j++) {
				num[i][j]=sc.nextInt();
			}
		}
		f(1);
		System.out.println(min);
	}
	public static void f(int a){
		if(a==n+1){
			if(sum<min){
				min=sum;
			}
			return;
		}
		for (int i = 1; i <=n; i++) {
			if(!bool[i]){
				sum+=num[a][i];
				bool[i]=true;
				f(a+1);
				sum-=num[a][i];
				bool[i]=false;
			}
		}
	}
}

Posted by jbachris on Tue, 16 Nov 2021 20:53:08 -0800