Algorithm Design and Analysis-Chapter 3 Recursion and Divide and Conquer Strategy

The design idea of the divide-and-conquer method is to divide a large problem that is difficult to solve directly into the same problems of smaller scale, so that each can be broken down and divided and ruled. If the original problem can be divided into k sub-problems (1 <k≤n), and these sub-problems can be solved, and the solutions of these sub-problems can be used to find the solution of the original problem, then this divide-and-conquer method is feasible. The sub-problem generated by the divide-and-conquer method is often a smaller model of the original problem, which provides convenience for the use of recursive techniques.
3.1 Recursive algorithm The
program's direct or indirect calling its programming skills is called recursive algorithm (Recursion).
Recursion needs to have boundary conditions, recursive forward segment and recursive return segment.

  • When the boundary conditions are not met, recursively advance;
  • When the boundary conditions are met, recursive return.
  • Note: When using the incremental recursion strategy, there must be a clear recursion end condition, called recursion exit, otherwise it will continue indefinitely (deadlock).

Disadvantages of recursion:

  • The efficiency of recursive algorithm to solve problems is low.
  • In the process of recursive call, the system opens up a stack to store the return point and local variables of each layer. Too many recursion times can easily cause stack overflow.

3.1.1 Fibonacci sequence
Insert picture description here
algorithm 3.1 Recursive algorithm of Fibonacci sequence

int fib(int n) 
{  
	if (n<=1) 
		return 1;  
	return fib(n-1)+fib(n-2); 
}

The efficiency of this algorithm is very low because the recursion is repeated too many times.
Algorithm 3.2 Recursive algorithm of Fibonacci sequence

int  fib[50];
void fibonacci(int n) 
{  
	fib[0] = 1;   
	fib[1] = 1;
	for (int i=2; i<=n; i++)   
		fib[i] = fib[i-1]+fib[i-2]; 
}

3.1.2 Algorithms for the complete permutation problem of
Insert picture description here
sets 3.3 Recursion of the permutation problem

void Perm(int list[], int k, int m) 
{
	if(k==m)  
	{   
		for(int i=0;i<=m;i++)    
			cout<<list[i]<<" ";   
		cout<<endl;
	}  
	else
		for(int j=k;j<=m;j++)   
		{
			swap(list[k],list[j]);    
			Perm(list,k+1,m);    
			swap(list[k],list[j]);   
		} 
}

3.1.3 Integer partition problem The
integer partition problem is one of the classic propositions in the algorithm. Represent a positive integer n as the sum of a series of positive integers: n = n1 + n2 +… + nk (where n1> = n2> =…> = nk> = 1, k> = 1)
This representation of positive integer n This is called the division of positive integer n. The number of different divisions of a positive integer n is called the division number of a positive integer n, and is denoted as p (n).
The positive integer 6 has the following 11 different divisions, so p (6) = 11.
6
5 + 1
4 + 2, 4 + 1 + 1
3 + 3, 3 + 2 + 1, 3 + 1 + 1 + 1
2 + 2 + 2, 2 + 2 + 1 + 1, 2 + 1 + 1 + 1 + 1
1 + 1 + 1 + 1 + 1 + 1 + 1
algorithm analysis
Insert picture description here
algorithm 3.4 partition algorithm of positive integer n

int split(int n,int m) 
{  
	if(n==1||m==1) 
		return 1;  
	else if (n<m) 
		return split(n,n);
	else if(n==m) 
		return split(n,n-1)+1;  
	else 
		return split(n,m-1)+split(n-m,m); 
}

3.2 Divide-and-conquer strategy The
divide-and-conquer strategy is for a problem of size n. If the problem can be easily solved (for example, if the size of n is small), it is directly solved, otherwise it is decomposed into k sub-problems of small size The sub-problems are independent of each other and have the same form as the original problem.
Solve these sub-problems recursively, and then combine the solutions of each sub-problem to get the original problem.
3.2.1 Basic steps of the
divide and conquer method The divide and conquer method has three steps on each level of recursion:

  1. Decomposition : Decompose the original problem into several sub-problems of smaller scale, independent of each other, and in the same form as the original problem;
  2. Solution : If the sub-problem is small and easy to be solved, directly solve it, otherwise solve each sub-problem recursively;
  3. Merge : merge the solutions of each sub-problem into the original problem.

Algorithm 3.5 Algorithm design pattern of divide and conquer strategy

Divide_and_Conquer(P) 
{  
	if (|P|<=n0 ) 
		return adhoc(P);  
	divide P into smaller substances P1,P2,…,Pk;
	for (i=1; i<=k; k++)    
		yi=Divide-and-Conquer(Pi)  
	Return merge(y1,y2,…,yk)      
}

When designing algorithms using the divide-and-conquer method, it is best to make the scale of the subproblems approximately the same. If divided into k sub-problems of equal size, many problems can take k = 2. This approach of making the sub-problems roughly equal is derived from the idea of ​​a balancing (Balancing) sub-problem, which is almost always better than the practice of unequal sub-problems.
3.2.2 Applicable conditions of the
divide and conquer method The problems that the divide and conquer method can solve generally have the following characteristics:

  1. This problem can be easily solved by reducing the scale to a certain extent;
  2. The problem can be decomposed into several identical problems of smaller scale, that is, the problem has the optimal substructure property;
  3. The solutions of the sub-problems decomposed by this problem can be combined into the solution of this problem;
  4. The sub-problems decomposed by this problem are independent of each other, that is, there is no common sub-problem among the sub-problems.

3.2.4 Round-robin schedule
Problem description: There are n = 2k athletes to play a round-robin tournament.
Now we need to design a game schedule that meets the following requirements:

  1. Each player must compete with other n-1 players once;
  2. Each player can only compete once a day;
  3. The round robin game ends in n-1 days.

Please design the game schedule as a table with n rows and n-1 columns according to this requirement. In the i-th row and j-th column of the table, fill in the player that the i-th player met on the j-th day, where 1≤i≤n, 1≤j≤n-1.

void Table(int k) 
{   
	int i, r;  
	int n = 1 << k; 
	for (i=0; i<n; i++)   
		a[0][i] = i + 1;
	for (r=1; r<n; r<<=1)   
		for (i=0; i<n; i+=2*r)   
		{     
			Copy(r, r + i, 0, i, r);  
			Copy(r, i, 0, r + i, r);
		} 
}

Realize the copy of square matrix

//源方阵的左上角顶点坐标(fromx, fromy),行列数为r 
//目标方阵的左上角顶点坐标(tox, toy),行列数为r 
void Copy(int tox, int toy, int fromx, int fromy, int r) 
{
	for (int i=0; i<r; i++)   
		for (int j=0; j<r; j++)      
			a[tox+i][toy+j] = a[fromx+i][fromy+j]; 
}

3.2.6 Selection problem
For a given n-element array a [0: n-1], it is required to find the k-th smallest element.
Input There
are multiple sets of test cases.
There are 2 lines for each test case, the first line is integers n and k (1≤k <n≤1000), and the second line is n integers.
Output
the k-th smallest element.
Use the idea of ​​quick sort algorithm to solve the selection problem.
After remembering a quick sort, the number of elements in the left subset is decomposed to be nleft, then the selection problem may be one of the following situations:

  1. nleft = k﹣1, then the boundary data is the answer to the selection question.
  2. nleft> k﹣1, then the answer to the selected question continues to be found in the left subset, and the size of the question becomes smaller.
  3. nleft <k﹣1, then the answer to the selection question continues to be found in the right subset, the question becomes to select the k-nleft-1 smallest number, and the scale of the question becomes smaller.

Algorithm 3.9 Algorithm for finding the k-th smallest element using a divide-and-conquer strategy

int select(int left,int right,int k)
{
	if(left>=right)
		return a[left];
	int i=left;
	int j=right+1;
	int pivot=a[left];
	while(true)
	{
		do{
			i=i+1;
		}while(a[i]<pivot);
		do{
			j=j-1;
		}while(a[j]>pivot);
		if(i>=j)
			break;
		swap(a[i],a[j]);
	}
		if(j-left+1==k)
			return pivot;
		a[left]=a[j];
		a[j]=pivot;
		if(j-left+1<k)
			return select(j+1,right,k-j+left-1);
		else
			return select(left,j-1,k);
}

3.2.7 Oil pipeline problem
An oil company plans to build a main oil pipeline from east to west. The pipeline will pass through an oil field with n oil wells. From each well, an oil pipeline must be connected to the main pipeline along the shortest route (or south or north).
If the positions of n oil wells are given, that is, their x-coordinates (east-west) and y-coordinates (north-south), how to determine the optimal position of the main pipeline, even if the sum of the length of the oil pipeline from each well to the main pipeline is the smallest s position?
Given the positions of n oil wells, program and calculate the sum of the minimum length of the oil pipeline from each well to the main pipeline.
Input
Line 1 is an integer n representing the number of oil wells (1≤n≤10 000). The next n rows are the positions of the oil wells, with two integers x and y per row (﹣ 10 000≤x, y≤10 000).
Output
The sum of the minimum length of the oil pipeline from each well to the main pipeline.
Suppose the positions of n oil wells are pi = (xi, yi), i = 1 ~ n. Since the main oil pipeline is east-west , the y coordinate of its main axis can be used to uniquely determine its position. The optimal position y of the main pipeline should meet:
Insert picture description here
from the median theorem, y is the median.
Algorithm 1: Sort the array a (usually in ascending order), take the middle element

int n;
int x;
int a[1000];
cin>>n; 
for(int k=0;k<n;k++)  
	cin>>x>>a[k];
sort(a,a+n);
int min=0; 
for(int i=0;i<n;i++)  
	min += (int)fabs(a[i]-a[n/2]); 
cout<<min<<endl;

Algorithm 2: Use the divide and conquer strategy to find the median

int n; 
int x; 
int a[1000]; 
cin>>n; 
for (int i=0; i<n; i++)  
	cin>>x>>a[i];
int y = select(0, n-1, n/2); 
int min=0; 
for(int i=0;i<n;i++)  
	min += (int)fabs(a[i]-y); 
cout<<min<<endl;

3.2.8 Half number set problem
Given a natural number n, starting from n, the numbers in the half number set set (n) can be generated in turn as follows.
(1) n set (n);
(2) Add a natural number to the left of n, but the natural number cannot exceed half of the recently added number;
(3) Process according to this rule until no more natural number can be added.
For example, set (6) = {6, 16, 26, 126, 36, 136}.
There are 6 elements in set (6).
Note that the half set is a multiple set .
For a given natural number n, program to calculate the number of elements in the set (n) of the half set.
If the number of elements in set (n) is f (n), then there are obviously:
Insert picture description here
Algorithm 3.12 Recursive algorithm for calculating half number set problem

int comp(int n) 
{  
	int ans=1;  
	if (n>1) 
		for(int i=1;i<=n/2;i++)   
			ans+=comp(i);  
	return ans; 
}

Algorithm 3.13 Recursive algorithm for computing half-number set problems-memory search

int a[1001]; 
int comp(int n) 
{  
	int ans=1;  
	if(a[n]>0)
		return a[n]; 
	for(int i=1;i<=n/2;i++)   
		ans+=comp(i);  
	a[n]=ans;
	return ans; 
}
Published 48 original articles · Like 25 · Visit 2453

Guess you like

Origin blog.csdn.net/qq_43628959/article/details/105106625