Professional Documents
Culture Documents
Combine
build an overall solution by combining the
partial solutions.
Recursive implementation.
Example
T(n)
Binary search:
Solve(Problem): (1) D(n) = (1), C(n) = (1)
If problem elementary:
a = 1, b = 2
Solution = Trivial_solve(Problem)
D(n) Recursion equation:
else: a subproblems
T(n) = T(n/2) + 1 n>1
Subproblem1,2,3,…,a = Divide(Problem) ;
For each Subproblemi: T(1) = 1 n=1
T(n/b)
Subsolutioni = Solve(Subproblemi) ; Solution: T(n) = O(logn)
Return Solution = Combine(Subsolution1,2,3,…,a) ;
C(n)
Algorithms and programming Divide and conquer
0 1 2 0 1 2
0, 1, 2: shafts 0, 1, 2
large disk Problem 000 222 decomposed into 3
subproblems:
medium disk
1. medium and small disks from 0 to 1 000 011
small disk
2. large disk from 0 to 2 011 211
0 means small disk on shaft 0, 2 means
large disk on shaft 2, etc. 3. Medium and small disks from 1 to 2. 211 222
state 011
state transition
Algorithms and programming Divide and conquer
(1)
Combine: no action
C(n) = (1)
Recursive sorting
algorithms
Recurrence equation :
T(n) = 2T(n-1) + 1 n>1
T(1) = 1 n=1
Solution: T(n) = O(2n)
Merge Sort
Recursion
Division:
merge sort on left subarray A[p..q]
• 2 subarrays (left and right) with respect
merge sort on right subarray A[q+1..r]
to the array’s middle element.
Termination condition: with 1 (p=r) or
p r
0 (p>r) elements the array is sorted
Ricombination:
Example
Recursive division: Ricombination:
12 6 4 5 9 2 3 1
12 6 4 5 9 2 3 1
12 6 4 5 9 2 3 1
6 12 4 5 2 9 1 3
12 6 4 5 9 2 3 1 4 5 6 12 1 2 3 9
12 6 4 5 9 2 3 1 1 2 3 4 5 6 9 12
Analysis
Assumption: n = 2k. Intuitively:
Divide: divide array in 2 D(n)=(1)
Solve: solve 2 subproblems of size n/2 each
2T(n/2)
log2 n
Quicksort
In place Recursion
Non stable quicksort on the left subarray A[p..q]
Division:
quicksort on the right subarray A[q+1..r]
• partition array A[p..r] in 2 subarrays:
termination condition: if the array has
given a pivot element x
Average case
n n
Provided we are not in the worst case,
though partitions may be strongly n/10 9n/10 n
unbalanced, average case = best case log10 n n/100 9n/100 9n/100 81n/100 n
Example: at each step generate 2
partitions, the first one with 9/10 n and the log10/9 n
second one with n/10 elements.
1
1 n
T(n) = O(n log n)
Pivot selection
S 1 2 3 4 N=4 M=3
Example #1: combinations
Array v: it represents the set S start =0
Array c: it represents the (current) combination pos = 0 1 2 3 4
i=0 i=1 i=2 i=3
At each step (pos), a new element from v is
added to c (in position pos)
start =1 start =2 start =3
The element to be added to c is “selected” by pos = 1 1 2 1 3 2 3 2 4
1 4 3 4
scanning v through an index i i=1 i=2 i=3 i=2 i=3 i=3
int comb(int *v, int n, int *c, int m, int comb(int *v, int n, int *c, int m,
int start, int pos, int count) int start, int pos, int count)
{ {
int i; int i;
if (pos >= m) { if (pos >= m) { termination:
for (i=0; i<m; i++) for (i=0; i<m; i++)
display solution
printf("%d ", c[i]); printf("%d ", c[i]);
printf("\n"); printf("\n");
return count+1; return count+1;
} }
for (i=start; i<n; i++) { for (i=start; i<n; i++) {
c[pos] = v[i]; c[pos] = v[i];
count = comb(v, n, c, m, i+1, pos+1, count); count = comb(v, n, c, m, i+1, pos+1, count);
} }
return count; return count;
} }
int comb(int *v, int n, int *c, int m, int comb(int *v, int n, int *c, int m,
int start, int pos, int count) int start, int pos, int count)
{ {
int i; int i;
if (pos >= m) { if (pos >= m) {
for (i=0; i<m; i++) for (i=0; i<m; i++)
printf("%d ", c[i]); printf("%d ", c[i]);
printf("\n"); printf("\n"); c[pos] filled with all v values
return count+1; return count+1; from the start position
stop recursion
} }
for (i=start; i<n; i++) { for (i=start; i<n; i++) {
c[pos] = v[i]; c[pos] = v[i];
count = comb(v, n, c, m, i+1, pos+1, count); count = comb(v, n, c, m, i+1, pos+1, count);
} }
return count; return count;
} }
void comb(int *v, int n, int *c, int m, int i, int tot) void comb(int *v, int n, int *c, int m, int i, int tot)
{ {
int j; int j;
if (i >= n) { if (i >= n) {
for (j=0; j<n; j++) for (j=0; j<n; j++)
termination:
if (c[j] != 0) if (c[j] != 0)
display solution
printf("%d ", v[j]); printf("%d ", v[j]);
printf("\n"); printf("\n");
return; return;
} }
if (n-i+tot > m) { if (n-i+tot > m) {
c[i] = 0; comb(v, n, c, m, i+1, tot); c[i] = 0; comb(v, n, c, m, i+1, tot);
} }
if (tot < m) { if (tot < m) {
c[i] = 1; comb(v, n, c, m, i+1, tot+1); c[i] = 1; comb(v, n, c, m, i+1, tot+1);
} }
} }
void comb(int *v, int n, int *c, int m, int i, int tot) void comb(int *v, int n, int *c, int m, int i, int tot)
{ {
int j; int j;
if (i >= n) { if (i >= n) {
for (j=0; j<n; j++) for (j=0; j<n; j++)
if (c[j] != 0) if (c[j] != 0)
printf("%d ", v[j]); printf("%d ", v[j]);
printf("\n"); printf("\n");
check wether we can skip the current element
return; return;
stop recursion
} }
if (n-i+tot > m) { if (n-i+tot > m) {
c[i] = 0; comb(v, n, c, m, i+1, tot); c[i] = 0; comb(v, n, c, m, i+1, tot);
} }
if (tot < m) { if (tot < m) {
c[i] = 1; comb(v, n, c, m, i+1, tot+1); c[i] = 1; comb(v, n, c, m, i+1, tot+1);
} }
} }
void comb(int *v, int n, int *c, int m, int i, int tot) void comb(int *v, int n, int *c, int m, int i, int tot)
{ {
int j; int j;
if (i >= n) { if (i >= n) {
for (j=0; j<n; j++) for (j=0; j<n; j++)
if (c[j] != 0) if (c[j] != 0)
printf("%d ", v[j]); printf("%d ", v[j]);
printf("\n"); printf("\n");
skip the current element recur over the next element
return; return;
} }
if (n-i+tot > m) { if (n-i+tot > m) {
c[i] = 0; comb(v, n, c, m, i+1, tot); c[i] = 0; comb(v, n, c, m, i+1, tot);
} }
if (tot < m) { if (tot < m) {
c[i] = 1; comb(v, n, c, m, i+1, tot+1); c[i] = 1; comb(v, n, c, m, i+1, tot+1);
} }
} }
Algorithms and programming Divide and conquer
void comb(int *v, int n, int *c, int m, int i, int tot) void comb(int *v, int n, int *c, int m, int i, int tot)
{ {
int j; int j;
if (i >= n) { if (i >= n) {
for (j=0; j<n; j++) for (j=0; j<n; j++)
if (c[j] != 0) if (c[j] != 0)
printf("%d ", v[j]); printf("%d ", v[j]);
printf("\n"); printf("\n");
return; return;
} }
if (n-i+tot > m) { if (n-i+tot > m) {
check n,
c[i] = 0; comb(v, wether
c, we
m, can addtot);
i+1, the current element addm,
c[i] = 0; comb(v, n, c, thei+1,
current element
tot);
} }
if (tot < m) { if (tot < m) {
c[i] = 1; comb(v, n, c, m, i+1, tot+1); c[i] = 1; comb(v, n, c, m, i+1, tot+1);
} }
} }
S 1 2 3 4 N=4 M=2
void comb(int *v, int n, int *c, int m, int i, int tot)
{
int j;
if (i >= n) { n-i+tot >m
for (j=0; j<n; j++) 4–0+0 >2
if (c[j] != 0)
printf("%d ", v[j]);
printf("\n");
return;
}
if (n-i+tot > m) { recur over the next element
c[i] = 0; comb(v, n, c, m, i+1, tot);
}
if (tot < m) {
c[i] = 1; comb(v, n, c, m, i+1, tot+1);
}
}
0 0
n-i+tot >m
4–1+0 >2 00
n-i+tot >m
4–2+0 >2
Algorithms and programming Divide and conquer
0 0
00 00
tot < m
0<2 0 01
n-i+tot >m
4–3+1 >2
0 0
00 00
00 1 0 01
tot < m
1<2 0 01 1
{1,2}
0 0
00 tot < m 00 01
0<2
n-i+tot >m
00 1 0 01 4–2+1 >2
00 11 0 01 1
Algorithms and programming Divide and conquer
0 0
00 01 00 01
00 1 01 0 0 01 01 0
0 0
00 01 00 01
tot < m
00 1 01 0 0 01 01 0
1<2
00 11 0 10 1 0 01 1 01 01
{1,3}
0 0
00 01 00 01
00 1 01 0 01 1 0 01 01 0 0 11
n-i+tot >m
00 11 0 10 1 4–3+2 >2 0 01 1 01 01 01 10
{2,3}
Algorithms and programming Divide and conquer
0<2
0
4-1+1 < 2 1 1<2
00 01 10 11
1<2 4-2+2< 2 2<2
00 1 01 0 01 1 10 0 1 01 1 10
4-3+1 < 2 1<2 4-3+2< 2 2 < 2 4-3+2< 2 2<2
tot < m
00 11 0 10 1 0 11 0 2<2 10 01 1 01 0 1 10 0
{1,4} {2,4} {3,4}
void perm(int *v, int *p, int *m, int n, int pos) void perm(int *v, int *p, int *m, int n, int pos)
{ {
int i; int i;
if (pos >= n) { if (pos >= n) {
for (i=0; i<n; i++) printf("%d ", p[i]); for (i=0; i<n; i++) printf("%d ", p[i]);
printf("\n"); printf("\n");
return; return; termination:
} } display solution
for (i=0; i<n; i++) { for (i=0; i<n; i++) {
if (m[i] == 0) { if (m[i] == 0) {
m[i] = 1; m[i] = 1;
p[pos] = v[i]; p[pos] = v[i];
perm(v, p, m, n, pos+1); perm(v, p, m, n, pos+1);
m[i] = 0; m[i] = 0;
} }
} }
} }
Algorithms and programming Divide and conquer
void perm(int *v, int *p, int *m, int n, int pos) void perm(int *v, int *p, int *m, int n, int pos)
{ {
int i; int i;
if (pos >= n) { if (pos >= n) {
for (i=0; i<n; i++) printf("%d ", p[i]); for (i=0; i<n; i++) printf("%d ", p[i]);
printf("\n"); printf("\n");
return; return; iterate over
stop recursion all values in v
} }
for (i=0; i<n; i++) { for (i=0; i<n; i++) {
if (m[i] == 0) { if (m[i] == 0) {
m[i] = 1; m[i] = 1;
p[pos] = v[i]; p[pos] = v[i];
perm(v, p, m, n, pos+1); perm(v, p, m, n, pos+1);
m[i] = 0; m[i] = 0;
} }
} }
} }
void perm(int *v, int *p, int *m, int n, int pos) void perm(int *v, int *p, int *m, int n, int pos)
{ {
int i; int i;
if (pos >= n) { if (pos >= n) {
for (i=0; i<n; i++) printf("%d ", p[i]); for (i=0; i<n; i++) printf("%d ", p[i]);
printf("\n"); printf("\n");
return; return;
} }
for (i=0; i<n; i++) { if a value has not been used yet for (i=0; i<n; i++) {
if (m[i] == 0) { if (m[i] == 0) { mark it
m[i] = 1; m[i] = 1;
p[pos] = v[i]; p[pos] = v[i];
perm(v, p, m, n, pos+1); perm(v, p, m, n, pos+1);
m[i] = 0; m[i] = 0;
} }
} }
} }
void perm(int *v, int *p, int *m, int n, int pos) void perm(int *v, int *p, int *m, int n, int pos)
{ {
int i; int i;
if (pos >= n) { if (pos >= n) {
for (i=0; i<n; i++) printf("%d ", p[i]); for (i=0; i<n; i++) printf("%d ", p[i]);
printf("\n"); printf("\n");
return; return;
} }
for (i=0; i<n; i++) { for (i=0; i<n; i++) {
if (m[i] == 0) { if (m[i] == 0) { recur over the next position
m[i] = 1; use it m[i] = 1;
p[pos] = v[i]; p[pos] = v[i];
perm(v, p, m, n, pos+1); perm(v, p, m, n, pos+1);
m[i] = 0; m[i] = 0;
} }
} }
} }
Algorithms and programming Divide and conquer
void perm(int *v, int *p, int *m, int n, int pos) 0 0 0 array m 1 2 3
{ ? ? ? array p
int i; array v
if (pos >= n) {
for (i=0; i<n; i++) printf("%d ", p[i]);
printf("\n"); 1 0 0 0 1 0 0 0 1
return; 1 ? ? 2 ? ? 3 ? ?
}
for (i=0; i<n; i++) {
if (m[i] == 0) {
m[i] = 1; 1 1 0 1 0 1 1 1 0 0 1 1 1 0 1 0 1 1
p[pos] = v[i]; 1 2 ? 1 3 ? 2 1 ? 2 3 ? 3 1 ? 3 2 ?
perm(v, p, m, n, pos+1);
m[i] = 0;
} unmark the used value 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1
}
}
1 2 3 1 3 2 2 1 3 2 3 1 3 1 2 3 2 1
Given a set S of positive integer values and an Array v: it represents the (sorted) set S
integer number N, generate all the sub-sets Y of Array m: it “flags” the values in S which have
S such that the sum of the elements in Y is been currently taken
equal to N Two integer variables (ps and t) represent the
sum of the selected elements and the sum of
Example: the elements still to consider, respectively
At each step (i), a new element from v is
S = {1, 2, 4, 6}, N = 7
analyzed:
result = {{1, 2, 4}, {1, 6}}
The element can be added if: ps+v[i] <= sum
The element can be skipped if: ps+t >= sum
It is possible to prove that 4 colors are starting from 0, edges represented through
enough in order to achieve the goal above the adjacency matrix
for any “planar” map int map[MAX][MAX] = {
{ 0, 1, 1, 0, 1, 1, 0},
{ 1, 0, 0, 0, 1, 1, 1}, 0 1
{ 1, 0, 0, 1, 1, 1, 1}, 4
{ 0, 0, 1, 0, 1, 0, 1}, 2 3
{ 1, 1, 1, 1, 0, 0, 1},
{ 1, 1, 1, 0, 0, 0, 1}, 6
5
{ 0, 1, 1, 1, 1, 1, 0}
};
such a way that none of them can eat any empty cell
of the other ones ● Termination (all queens placed): check
whether the queens’ configuration is valid
Optimization
0 8 0
1 9 1 0
2 10 2 1
3 11 3 2
4 12 4 3
5 13 5 4
6 14 6 5
7 7 6
0 1 2 3 4 5 6 7 14 13 12 11 10 9 8 7
ith
row (implicitly) occupied by the ith queen set_queen(i, j, …): place the ith queen on
column j, marking the proper elements of
queen[i] = index of the column where the ith
arrays col, diag and revdiag
queen has been placed
rem_queen(i, j, …): remove the ith queen
the ith queen, placed in column j, can eat from column j, cleaning the proper cells of
any other queen on: arrays col, diag and revdiag
• the jth column: col[j] place_recur(n, …): recursively place the nth
• the (i+j)th diagonal: diag[i+j] queen in row n (all columns are tried).
Termination: all queens have been placed.
• the (i-j)th reverse diagonal: revdiag[i-j+7]
Algorithms and programming Divide and conquer