一、分治法的求解过程:
(1)划分:既然是分治,当然需要把规模为n的原问题划分为k个规模较小的子问题,并尽量使这k个子问题的规模大致相同。
(2)求解子问题:各子问题的解法与原问题的解法通常是相同的,可以用递归的方法求解各个子问题,有时递归处理也可以用循环来实现。
(3)合并:把各个子问题的解合并起来,合并的代价因情况不同有很大差异,分治算法的有效性很大程度上依赖于合并的实现。
典型的递推方程:
迭代法可求得
典型案例:
一、全排列问题:
问题:R是由n个元素构成的序列集合,R={r1, r2, … ,rn},求R的全排列perm(R)。
问题理解:
(1) 若R中只有1个元素{r},则perm(R)=(r)
(2) 若R中只有2个元素{r1, r2},则
perm(R)=(r1)perm(R1)∪(r2)perm(R2)
其中,Ri=R-{ri}
(3) 若R中有3个元素{ r1, r2, r3},则
perm(R)=(r1)perm(R1)∪(r2)perm(R2)∪(r3)perm(R3)
分析:
思想:
依次将待排列的数组的后n-1个元素与第一个元素交换,则每次递归处理的都是后n-1个元素的全排列。当数组元素仅有一个时为此递归算法的出口。
代码展示:
#include<iostream>
using namespace std;
void Perm(int *arr, int k, int m) //m是数组最后一个元素的下标
{
if(k == m)
{
for(int i = 0; i<m; i++)
{
cout<<arr[i];
}
cout<<endl;
}
else
{
for(int i=k; i<m; i++)
{
swap(arr[k],arr[i]);
Perm(arr,k+1,m);
swap(arr[k],arr[i]);
}
}
}
int main()
{
int arr[] = {1,2,3};
Perm(arr,0,sizeof(arr)/sizeof(arr[0]));
return 0;
}
二、整数划分
问题:将给定正整数n表示成一系列正整数之和
n=n1+n2+…+nk,其中n1≥n2≥…≥nk≥1,k≥1。
求正整数n的不同划分个数p(n)。
举例:
正整数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;
代码展示:
int Q(int n, int m) //n是要划分的数,最大加数n1不大于m
{
if( (n < 1) || (m < 1) )
{
return 0;
}
if((n == 1) || (m == 1))
{
return 1;
}
if(n < m)
{
return Q(n,n);
}
if(n == m)
{
return Q(n,m-1) + 1;
}
return Q(n,m-1) + Q(n-m,m);
}
int main()
{
int m = 5;
int n = 5;
cout<<Q(n, m)<<endl;
return 0;
}
问题扩展:苹果装盘
三、最近点对问题
问题:给定平面上的n个点,找其中的一对点,使得在n个点组成的所有点对中,该点对之间的距离最小。
常规思想:只要将每一点与其它n-1个点的距离算出,找出其中距离最小的两点即可。问题:效率太低,T(n)=O(n2)
分治思想:将平面上n个点的集合S划分为两个子集S1和S2,每个子集约n/2个点,然后在每个子集中寻找最接近点对。
问题在于合并:如何根据子集中的最接近点对求得原集合中的最接近点对。
解决步骤:
bool Cpair2(S,d) {
n=|S|;
if (n < 2) {d=∞; return false;}
1、m=S中各点x间坐标的中位数;
构造S1和S2;
//S1={p∈S|x(p)<=m},
//S2={p∈S|x(p)>m}
2、Cpair2(S1,d1);
Cpair2(S2,d2);
3、dm=min(d1,d2);
4、设P1是S1中距垂直分割线l的距离在dm之内的所有点组成的集合;
P2是S2中距分割线l的距离在dm之内所有点组成的集合;
将P1和P2中点依其y坐标值排序;
并设X和Y是相应的已排好序的点列;
5、通过扫描X以及对于X中每个点检查Y中与其距离在dm之内的所有点(最多6个)可以完成合并;
当X中的扫描指针逐次向上移动时,Y中的扫描指针可在宽为2dm的区间内移动;
设dl是按这种扫描方式找到的点对间的最小距离;
6、d=min(dm,dl);
return true;}
四、棋盘覆盖
当k>0时,将2k×2k棋盘分割为4个2k-1×2k-1 子棋盘(a)所示。
特殊方格必位于4个较小子棋盘之一中,其余3个子棋盘中无特殊方格。为了将这3个无特殊方格的子棋盘转化为特殊棋盘,可以用一个L型骨牌覆盖这3个较小棋盘的会合处,如 (b)所示,从而将原问题转化为4个较小规模的棋盘覆盖问题。递归地使用这种分割,直至棋盘简化为棋盘1×1