分治法
基本思想
将一个难以直接解决的大问题,分解为规模较小的相同子问题,直至这些子问题容易直接求解,并且可以利用这些子问题的解求出原问题的解。各个击破,分而治之。
适用特征
- 该问题的规模小到一定的程度就可以容易解决
- 该问题可以分解为若干个规模较小的相同问题
- 利用该问题分解出的子问题的解可以合并为该问题的解
- 该问题所分解出的各个子问题是相互独立的,即子问题之间不包含公共的子问题
基本步骤:分解、求解子问题、合并
divide-and-conquer(P)
{
if(|P|<=n_0)adhoc(P);//求解最小的子问题
divide P into smaller subinstances P_1,P_2,...,P_k;//分解
for(i=1;i<=k;i++)
y_i = divide-and-conquer(P_i);//递归求解子问题
return merge(y_1,...,y_k);//合并
}
求解方法
- 迭代法
- 递归树
例题
1.全排列问题
问题
R R R是由n个元素构成的序列集合, R = { r 1 , r 2 , . . . , r n } R=\{r_1,r_2,...,r_n\} R={r1,r2,...,rn},求R的全排列 p e r m ( R ) perm(R) perm(R)
思路
-
若R中只有一个元素 r {r} r,则 p e r m ( R ) = ( r ) perm(R)=(r) perm(R)=(r)
-
若R中只有两个元素 { r 1 , r 2 } \{r_1,r_2\} {r1,r2},则 p e r m ( R ) = ( r 1 ) p e r m ( R 1 ) ∪ ( r 2 ) p e r m ( R 2 ) perm(R)=(r_1)perm(R_1)\cup (r_2)perm(R_2) perm(R)=(r1)perm(R1)∪(r2)perm(R2),其中, R i = R − { r i } R_i=R-\{r_i\} Ri=R−{ri}
-
若R中有3个元素 { r 1 , r 2 , r 3 } \{r_1,r_2,r_3\} {r1,r2,r3},则 p e r m ( R ) = ( r 1 ) p e r m ( R 1 ) ∪ ( r 2 ) p e r m ( R 2 ) ∪ ( r 3 ) p e r m ( R 3 ) perm(R)=(r_1)perm(R_1)\cup (r_2)perm(R_2)\cup (r_3)perm(R_3) perm(R)=(r1)perm(R1)∪(r2)perm(R2)∪(r3)perm(R3)
p e r m ( R ) = { r n = 1 ∪ ( r i ) p e r m ( R i ) n > 1 perm(R)= \begin{cases} r & n=1\\ \cup(r_i)perm(R_i)& n>1 \end{cases} perm(R)={r∪(ri)perm(Ri)n=1n>1
代码
template<class Type>
inline void Swap(Type& a,Type& b){
Type temp = a;
a = b;
b = temp;
}
template<class Type>
void Perm(Type list[],int k,int m){ //产生list[k:m]的所有排列
if(k == m){ //只剩下一个元素
for(int i=0;i<=m;i++)
cout<<list[i];
cout<<endl;
}
else{
for(int i=k;i<=m;i++){ //递归产生排序
Swap(list[k],list[i]);
Perm(list,k+1,m);
Swap(list[k],list[i]);
}
}
}
2.整数划分问题
问题
将正整数 n n n表示成一系列正整数之和, n = n 1 + n 2 + . . . + n k n=n_1+n_2+...+n_k n=n1+n2+...+nk,其中 n 1 > = n 2 > = . . . > = n k > = 1 , k = 1 n_1>=n_2>=...>=n_k>=1,k=1 n1>=n2>=...>=nk>=1,k=1。
求正整数 n n n的不同划分个数 p ( n ) p(n) p(n)
思路
q
(
n
,
m
)
q(n,m)
q(n,m):表示最大加数
n
1
n_1
n1不大于m的划分个数
q
(
n
,
m
)
=
{
1
n
=
1
,
m
=
1
q
(
n
,
n
)
m
>
n
1
+
q
(
n
,
n
−
1
)
m
=
n
q
(
n
,
m
−
1
)
+
q
(
n
−
m
,
m
)
1
<
m
<
n
q(n,m)= \begin{cases} 1 & n = 1,m = 1\\ q(n,n) & m > n\\ 1+q(n,n-1) & m = n\\ q(n,m-1)+q(n-m,m)& 1 < m < n \end{cases}
q(n,m)=⎩⎪⎪⎪⎨⎪⎪⎪⎧1q(n,n)1+q(n,n−1)q(n,m−1)+q(n−m,m)n=1,m=1m>nm=n1<m<n
代码
int q(int n,int m){ //n的最大加数不超过m
if(n == 1||m == 1)return 1;
else if(m > n)return q(n,n);
else if(m == n)return 1+q(n,m-1);
else if (m>1 &&m<n)return q(n,m-1)+q(n-m,m);
else return 0;
}
3.汉诺塔问题
4.二分搜索
问题
给定已按升序排好序的 n n n个元素 a [ 0 : n − 1 ] a[0:n-1] a[0:n−1],现要在这 n n n个元素中找出一个特定元素 x x x
代码
template<class Type>
int BinarySearch(Type a[],const Type& x, int n){ //搜索x
int left = 0;
int right = n-1;
while(left <= right){
int middle = (left+right)/2;
if(a[middle] == x)return middle;
if(a[middle] > x)right = middle-1;
else left = middle+1;
}
return -1;
}
5.合并排序
问题
给定一个可排序的 n n n个元素序列,对他们按照非降序方式重新排列
思想
将待排序元素分成大致相同的两个子集,分别进行排序后进行合并
代码
template<class Type>
void Merge(Type a[],int left,int i,int right){
int length = right-left+1;
int list_l = left;
int list_r = i+1;
int k;
Type *b = new Type[length];
for(k = 0;k<length&&list_l<i+1&&list_r<right+1;k++){
if(a[list_l]<a[list_r]){
b[k] = a[list_l];
list_l += 1;
}
else{
b[k] = a[list_r];
list_r += 1;
}
}
//处理剩余的某个数组
if(list_l !=i+1){//第一个数组有剩余
for(;k<length;k++)b[k] = a[list_l++];
}
if(list_r != right+1){//第二个数组有剩余
for(;k<length;k++)b[k] = a[list_r++];
}
for(int j=0;j<length;j++){
a[left+j] = b[j];
}
delete[] b;
}
template<class Type>
void MergeSort(Type a[],int left,int right){
if(left<right){
int i = (left+right)/2;
MergeSort(a,left,i);
MergeSort(a,i+1,right);
Merge(a,left,i,right);
}
}
6.快速排序
问题
给定一个可排序的 n n n个元素序列,对他们按照非降序方式重新排列
思想
对待排序数组不断拆分,拆分的同时不断交换元素,归为分裂点元素。记录的比较和交换是从两端向中间进行,记录每次移动的距离较大,因而总的比较和移动次数较少
代码
template<class Type>
int Partition(Type a[],int p,int r){
int i = p,j = r+1;
Type x = a[p]; //基准元素
//将小于x的元素交换到左边区域,将大于x的元素交换到右边区域
while(true){
while(a[++i] < x && i < r);
while(a[--j] > x);
if(i >= j)break;
Swap(a[i],a[j]);
}
a[p] = a[j];
a[j] = x;
return j;
}
template<class Type>
void QuickSort(Type a[],int p,int r){
int q = Partition(a,p,r); //以p为分裂点,整理p点两边的元素
QuickSort(a,p,q-1);
QuickSort(a,q+1,r);
}