算法 64式 6、分治算法整理

本文介绍了分治算法的思想,包括其含义、特点、适用条件和通用解法。通过最大最小问题举例说明分治策略,并探讨了分治与递归的区别。此外,还列举了分治算法的相关学习资源。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

1算法思想

分治

1.1含义

将规模为n的问题,分解为k个规模较小的子问题。每个子问题独立且与原问题相同,递归求解子问题,将各个子问题的解合并得到原问题的解。

1.2特点

分治三步骤: 划分,递归,合并

1.3适用

缩小,最优,合并,独立

1) 规模缩小到一定程度就容易解决

2) 问题最优子结构性质(即问题的最优解包含子问题的最优解),问题可以分解为规模较小的子问题

3) 子问题的解可以合并处理后得到问题的解

4) 子问题独立

1.4通用解法

分治算法:

       划分

       递归

       合并

1.5经典例题讲解

最大最小问题

企业老板有一袋金块,要从中挑选最重的一块给他的

优秀员工,挑选最轻的一块给他的一位一般员工。

代码:

pair<int,int> maxMin(int* pArr,int iLow,int iHigh)

{

       int iMax,iMin;

       if(iHigh - iLow <= 1)//递归出口,如果只剩一个元素

       {

              if(pArr[iLow] < pArr[iHigh])//区分谁是最大和谁是最小

              {

                     return pair<int,int>(pArr[iLow],pArr[iHigh]);

              }

              else

              {

                     return pair<int,int>(pArr[iHigh],pArr[iLow]);

              }

       }

       else//如果存在多个元素进行划分

       {

             

              int iMid = iLow + (iHigh - iLow)/2;                  //分治第一步,划分

              pair<int,int> pairRes1 = maxMin(pArr,iLow,iMid);     //分治第二步,递归求解

              pair<int,int> pairRes2 = maxMin(pArr,iMid+1,iHigh);

 

             

              iMin = min(pairRes1.first,pairRes2.first);             //分治第三步,合并

              iMax = max(pairRes1.second,pairRes2.second);

             

             

              return pair<int,int>(iMin,iMax);

       }

}

 

1.6 分治与递归的区别

分治中使用了递归,与递归不同的是,分治中是对划分后的多个子问题递归求解后,并对各个子问题的解做了归并的操作。

即分治比递归多了归并子问题解的操作。

 

2 分治系列

类别-编号

题目

遁去的1

1

最大最小问题:

企业老板有一袋金块,要从中挑选最重的一块给他的优秀员工,挑选最轻的一块给他的一位一般员工

 

输入:

9

8 3 6 2 1 9 4 5 7

输出:

1 9

算法设计与分析

https://blog.youkuaiyun.com/qingyuanluofeng/article/details/47189209

关键:

1 采用分治法:划分为两部分,比较两部分钟的最小值和最大值,即可,注意由于每次返回的是最大和最小,因此用pair做

2    if(iHigh - iLow <= 1)//递归出口,如果只剩一个元素

       {

              if(pArr[iLow] < pArr[iHigh])//区分谁是最大和谁是最小

              {

                     return make_pair<int,int>(pArr[iLow],pArr[iHigh]);

              }

 

代码:

 

pair<int,int> maxMin(int* pArr,int iLow,int iHigh)

{

       int iMax,iMin;

       if(iHigh - iLow <= 1)//递归出口,如果只剩一个元素

       {

              if(pArr[iLow] < pArr[iHigh])//区分谁是最大和谁是最小

              {

                     return pair<int,int>(pArr[iLow],pArr[iHigh]);

              }

              else

              {

                     return pair<int,int>(pArr[iHigh],pArr[iLow]);

              }

       }

       else//如果存在多个元素进行划分

       {

              //分治第一步,划分

              int iMid = iLow + (iHigh - iLow)/2;

              pair<int,int> pairRes1 = maxMin(pArr,iLow,iMid);

              pair<int,int> pairRes2 = maxMin(pArr,iMid+1,iHigh);

 

              //分治第二步,递归求解

              iMin = min(pairRes1.first,pairRes2.first);

              iMax = max(pairRes1.second,pairRes2.second);

             

              //分治第三步,合并

              return pair<int,int>(iMin,iMax);

       }

}

2

正整数划分

将正整数n表示成一系列正整数之和:

n = n1 + n2 + ... +nk

其中n1>=n2>=...>=nk>=1,k>=1

正整数的这种表示称为正整数n的划分。求正整数n的不同划分个数

 

输入:

6

输出:

11

算法设计与分析

https://blog.youkuaiyun.com/qingyuanluofeng/article/details/47189223

分析:设p(n)表示正整数n的划分树,难以找到递归关系,考虑增加变量

p(n) = q(n,n)

设q(n,m)表示:对于数字n,将最大加数n1不大于m的划分个数

q(n,m)={1,n = m = 1

          {q(n,n),n<m,正整数n的划分由n1=n的划分和n1<=n-1的划分组成

          {1+q(n,m-1),n=m,包含n只有1个划分,不包含n,划分值比n小

          {q(n,m-1) + q(n-m,m),n>m>1,正整数n的最大加数n1不大于m的划分由n1=m的划分和n1<=m-1的划分组成

包含m时,剩余的数为n-m,又因为还有可能包含m,因此是q(n-m,m)

不包含m的划分,划分中所有值比m小,所以是q(n,m-1)

 

代码:

long long g_q[MAXSIZE][MAXSIZE];

long long divideNum(int n,int m)

{

       if(g_q[n][m] != -1)

       {

              return g_q[n][m];

       }

       if(n == 1 || m == 1)

       {

              return g_q[n][m]= 1;

       }

       if(n < m)

       {

              return divideNum(n,n);

       }

       if(n == m)

       {

              return g_q[n][m] = 1 + divideNum(n,m-1);

       }

       if(n > m && m > 1)

       {

              return g_q[n][m] = divideNum(n,m-1) + divideNum(n-m,m);

       }

}

3

多项式乘积的分治方法:

计算两个n阶多项式的乘法:

p(x) = a0 + a1*x + a2*x^2 + a3*x^3 + ... + an*x^n

q(x) = b0 + b1*x + b2*x^2 + b3*x^3 + ... + bn*x^n

算法设计与分析

https://blog.youkuaiyun.com/qingyuanluofeng/article/details/47189251

为减少乘法运算次数,考虑把一个多项式划分成两个多现实

p(x) = p0(x) + p1(x)*x^n/2

q(x) = q0(x) + q1(x)*x^n/2

p(x)*q(x) = p0(x)*q0(x) + (p0(x)*q1(x) + p1(x)*q0(x))x^n/2 + p1(x)q1(x)*x^n//四个多项式乘法

          = p0(x)*q0(x) + ((p0(x) - p1(x))*(q1(x) - q0(x)) + p1(x)*q1(x) + p0(x)*q0(x))x^n/2 + p1(x)q1(x)*x^n//三个多项式乘法

4

Strassen矩阵乘法

A和B的乘积矩阵C中的元素C[i,j]定义为:C[i][j] = k从1到n 累加A[i][k]*B[k][j]

若依此定义来计算A和B的乘积矩阵C,则每计算C的一个元素C[i][j],需要做n次乘法和n-1次加法。因此,算出矩阵C的 个元素所需的计算时间为O(n3)

算法设计与分析

https://blog.youkuaiyun.com/qingyuanluofeng/article/details/47189261

分治法:

将矩阵A,B和C中的每一矩阵都分成4个大小相等的子矩阵。可以将方程C=AB重写为:

[C11 C12]  =  [A11 A12]  [B11 B12]

[C21 C22]     [A21 A22]  [B21 B22]

可以得到

C11 = A11*B11 + A12*B21

C12 = A11*B12 + A12*B22

C21 = A21*B11 + A22*B21

C22 = A21*B12 + A22*B22

为了降低时间复杂度,必须减少乘法的次数:

从8次乘法降为7次,用了7次对于n/2矩阵乘的递归调用和18次n.2矩阵的加减法计算

M1 = A11(B12 - B22)

M2 = (A11+A12)*B22

以下代码是转载的,有空的时候再研究

5

棋盘覆盖

在一个2k×2k 个方格组成的棋盘中,恰有一个方格

与其它方格不同,称该方格为一特殊方格,且称该棋

盘为一特殊棋盘。在棋盘覆盖问题中,要用图示的4种

不同形态的L型骨牌覆盖给定的特殊棋盘上除特殊方格

以外的所有方格,且任何2个L型骨牌不得重叠覆盖。

当k>0时,将2k×2k棋盘分割为4个2k-1×2k-1 子棋盘

(a)所示。特殊方格必位于4个较小子棋盘之一中,其

余3个子棋盘中无特殊方格。为了将这3个无特殊方格

的子棋盘转化为特殊棋盘,可以用一个L型骨牌覆盖这

3个较小棋盘的会合处,如 (b)所示,从而将原问题转

化为4个较小规模的棋盘覆盖问题。递归地使用这种分

割,直至棋盘简化为棋盘1×1。

 

输入:

行 列 k(2^k为棋盘长度)

0 1 2(左上角测试)

1 3 2(右上角测试)

3 0 2(左下角测试)

2 2 2 (右下角测试)

输出:

2 0 3 3

2 2 1 3

4 1 1 5

4 4 5 5

2 2 3 3

2 1 3 0

4 1 1 5

4 4 5 5

2 2 3 3

2 1 1 3

4 4 1 5

0 4 5 5

2 2 3 3

2 1 1 3

4 1 0 5

4 4 5 5

算法设计与分析

https://blog.youkuaiyun.com/qingyuanluofeng/article/details/47189269

关键:

1将2k×2k棋盘分割为4个2k-1×2k-1 子棋盘

(a)所示。特殊方格必位于4个较小子棋盘之一中,其

余3个子棋盘中无特殊方格。为了将这3个无特殊方格

的子棋盘转化为特殊棋盘,可以用一个L型骨牌覆盖这

3个较小棋盘的会合处,

2如果不在棋盘左上,就设置右下角为指定颜色

                     右上,           左下

                     左下              右上

                     右下              左上

3    //分治第二步:递归处理

       //判断特殊标记是否在左上角

       if(iColorRow < iBegRow + iHalfSize && iColorCol < iBegCol + iHalfSize)

       {

              //如果特殊标记在左上角,那么左上角区域递归处理

              chessCover(iBegRow,iBegCol,iColorRow,iColorCol,iHalfSize,pChessBoard);

       }

       else

       {

              //如果不在左上角,那么设置左上角区域中的最右下角位置为特殊标记。区域重新设置,起始行列:要随区域的不同而改变,左上不需改变,右上的列需要改为原列+棋盘半长

              pChessBoard[iBegRow + iHalfSize - 1][iBegCol + iHalfSize - 1] = iMarkNum;

  &nb

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值