Introduction to Divide-and-Conquer
1. Recursion
According to wikipedia, Recursion is the process of repeating itself in a self-similar way. A good case in point is the procedure of Euclidean Algorithm:
1 public static int gcd(int m,int n,int[] ref) { 2 // Calculate the greatest common divisor of m and n 3 // Precondition: n>=0 && m>0 && ref.length>=2 4 // Postcondition: the gcd of m and n is returned 5 // and gcd(m,n) = m*ref[0]+n*ref[1] 6 if (n==0) { 7 ref[0] = 1; 8 ref[1] = 0; 9 return m; 10 } else { 11 int val = gcd(n,m%n,ref); 12 int y = ref[1]; 13 ref[1] = ref[0]-m/n*y; 14 ref[0] = y; 15 return val; 16 } 17 }
2. Divide-and-Conquer Algorithms
Sometimes it is entirely possible for us to gain a better time performance if we divide the problem into several sub-problems and solve them recursively before finally accomplishing the total task. This is what we call the family of Divide-and-Conquer Algorithms, and I shall give you some examples:
(1) To calculate the nth power of a natural number x, instead of multiplying x to the temporary result repeatedly, we'd better use the following method:
1 public static long pow(int x,int n) { 2 // Return the nth power of a natural number x 3 if (n==0) { 4 return 1; 5 } else { 6 long tmp = pow(x,n>>1); 7 if ((n&1)!=0) { 8 return tmp*tmp*x; 9 } else { 10 return tmp*tmp; 11 } 12 } 13 }
(2) Some sorting algorithms, such as Quick Sort, Merge Sort and so forth.
(3) Multiplication of two big integers, a trick invented by Gauss.
(4) Multiplication of two big matrices, what is called Strassen Algorithm.
(5) Multiplication of two polynomials, the well-known Fast Fourier Transform.
(6) Determining the nearest point pair among all the given points in a plane.
3. Master Theorem
To determine the time complexity of a divide-and-conquer algorithm remains to be a tricky work since some recursions are strikingly intractable. Whereas there is a theorem that can assist us a lot in most cases. The following figure is by courtesy of the renowned algorithm cookbook CLRS:
4. My Solution to USACO Problem "rect1"
Here I wish to show you an example that I used a divide-and-conquer method to solve a USACO training problem called Shaping Regions:
1 import java.io.*; 2 import java.util.*; 3 4 public class rect1 { 5 public static Scanner input; 6 public static PrintWriter output; 7 public static int num; // number of rectangles 8 public static int [] color; // areas of different colors 9 public static int [][] rect; // rectangle information 10 11 public static int cover(int i,int x1,int y1,int x2,int y2) { 12 // The area of Rect(x1,y1,x2,y2) that is not covered 13 // by any one from Rect[i] to Rect[num] 14 if (x1>=x2||y1>=y2) { 15 return 0; 16 } else if (i>num) { 17 return (x2-x1)*(y2-y1); 18 } 19 // calculate the overlapping part Rect(a,b,c,d) 20 int a = (rect[i][0]>x1)? rect[i][0]:x1; 21 int b = (rect[i][1]>y1)? rect[i][1]:y1; 22 int c = (rect[i][2]<x2)? rect[i][2]:x2; 23 int d = (rect[i][3]<y2)? rect[i][3]:y2; 24 if (a>=c || b>=d) { 25 // no overlapping area 26 return cover(i+1,x1,y1,x2,y2); 27 } else { 28 // divide the remaining part into 8 parts 29 int val = 0; 30 val += cover(i+1,x1,y1,a,b); 31 val += cover(i+1,a,y1,c,b); 32 val += cover(i+1,c,y1,x2,b); 33 val += cover(i+1,c,b,x2,d); 34 val += cover(i+1,c,d,x2,y2); 35 val += cover(i+1,a,d,c,y2); 36 val += cover(i+1,x1,d,a,y2); 37 val += cover(i+1,x1,b,a,d); 38 return val; 39 } 40 } 41 public static void getInput() throws IOException { 42 // Get the input data: 43 input = new Scanner(new FileReader("rect1.in")); 44 color = new int [2500]; 45 int wid = input.nextInt(); 46 int len = input.nextInt(); 47 num = input.nextInt(); 48 rect = new int [num+1][5]; 49 // consider background as Rect[0] 50 rect[0][2] = wid; 51 rect[0][3] = len; 52 for (int i=1;i<=num;i++) { 53 // following info of rectangles 54 rect[i][0] = input.nextInt(); 55 rect[i][1] = input.nextInt(); 56 rect[i][2] = input.nextInt(); 57 rect[i][3] = input.nextInt(); 58 rect[i][4] = input.nextInt()-1; // color 59 } 60 input.close(); 61 } 62 public static void printAns() throws IOException { 63 // Print the final answer: 64 output = new PrintWriter(new FileWriter("rect1.out")); 65 for (int i=0;i<2500;i++) { 66 if (color[i]>0) { 67 output.println(i+1+" "+color[i]); 68 } 69 } 70 output.close(); 71 } 72 public static void main(String[] args) throws IOException { 73 getInput(); 74 for (int i=0;i<num;i++) { 75 // the area of Rect[i] that can be shown ultimately 76 color[rect[i][4]] += 77 cover(i+1,rect[i][0],rect[i][1],rect[i][2],rect[i][3]); 78 } 79 // the top rectangle is covered by none 80 color[rect[num][4]] += 81 (rect[num][2]-rect[num][0])*(rect[num][3]-rect[num][1]); 82 printAns(); 83 } 84 }
References:
1. Cormen, T. H. et al. Introduction to Algorithms[M]. 北京:机械工业出版社,2006-09
2. Dasgupta, Sanjoy, Christos Papadimitriou, and Umesh Vazirani. Algorithms[M].北京:机械工业出版社,2009-01-01