分治法的基本思想:把一个规模为n的问题分解为k个规模较小的自问题,这些子问题互相独立且与原问题相同。
一般的算法设计模式:
Divide-and-Conquer(P)
{
if(|P|<=n0)
Adhoc(P);
divide P into smaller subinstances
P1,P2,...,Pk;
for(int i = 1;i <= k;++i)
yi = Divide-and-Conquer(Pi);
return Merge(y1,y2,...,yk);
}
|P|表示问题的规格
n0表示当P规模不超过n0那么问题就已经容易解决不需要去分解
Adhoc(P)是该分治算法的基本子算法,当规模不超过n0就可以直接用它得出结果
Merge()的作用是该分治算法中的合并子算法,将所有子问题的解合并成P的解
例子,就是一个合并排序
template<class Type>
void Merge(Type *sd,Type *si,int left,int m,int right)
{
int i = left,j = m+1;
int k = left;
while(i<=m && j<= right)
{
sd[k++] = si[i] < si[j]? si[i++]:si[j++];
}
while(i<=m)
{
sd[k++] = si[i++];
}
while(j<=right)
{
sd[k++] = si[j++];
}
}
template<class Type>
void MergePass(Type *ar,Type *br,int n,int s)
{// s = 2;
// 0 1 2 3 //4 5 6 7 //8 9
cout<<"s = "<<s<<endl;
for(int i = 0;i+2*s-1 <= n-1; i+=2*s)
{
Merge(ar,br,i,i+s-1,i+2*s-1);
cout<<i<<" "<<i+s-1<<" "<<i+2*s-1<<endl;
}
if(n-1 > i+s-1)
{
Merge(ar,br,i,i+s-1,n-1);
cout<<i<<" "<<i+s-1<<" "<<n-1<<endl;
}
else//两个作用,一个是预防后面没有值了,还有一个是确保br给了ar,这个看后面你就懂了
{
for(int j = i;j<n;++j)
{
ar[j] = br[j];
}
}
Print_Array(ar,n);
}
template<class Type>
void MergeSort(Type *ar,int n)
{
Type *br = new Type[n];
int s = 1;
while(s < n)
{
MergePass(br,ar,n,s);//分治,分成小段小段,顺便的MergePass完成了分治中的合并子算法,和子算法的计算
s+=s;
MergePass(ar,br,n,s);//同上,顺便把所有数据给ar
s+=s;
}
delete []br;
}
在用分治法设计算法时,最好使子问题的规模大致相同,即将一个问题分成大小相等的k个子问题的处理方法是行之有效的。
分治算法棋盘覆盖的思路:
采用的是L型骨牌覆盖棋盘
(1)先看2*2,有一个点和其他的不同,另外三个被同一个L给覆盖了
(2)递推2^2*2^2,如果左上的2*2有,那么[1][2],[2][2],[2][1]构成了一个L,然后再分看,四个部分各一个L就补全了
要是8*8那么[3][4],[4][3],[4][4],找出规律,n*n,[n/2-1][n/2],[n/2][n/2-1],[n][n]构成第一个,然后n=n/2,一直到n=1为止
void CheseBoard(Array ar,int tr,int tc,int dr,int dc,int size)
dr,dc表示特殊点所在的位置,行列,tr,tc,棋盘左上角放个行号,棋盘左上角方格列号