• 分治法思考题


     一.求解a^n

    1.问题描述:

    采用分治策略来求解a^n。

    2.问题分析:

    (1)如果采用常规方法,n个a相乘,算法的复杂度是O(n)

    (2)如果采用分治策略,算法的复杂度则可大大减少

    a.当n为偶数时:a^n =(a^n/2) * a^n/2;
    b.当n为奇数时:a^n = a^(n-1)/2 * a^(n-1)/2*a;
    c.当n=1时: a^n=a;
    此时复杂度减少为O(logn)

    3.Code:

    本题采用递归分治的方法进行求解。 

    1. #include
    2. using namespace std;
    3. typedef long long ll;
    4. //递归求解(位运算会快一些)
    5. ll fac(int a, int n){
    6. if (n == 1) return a;
    7. if (n & 1)//判断n是否为奇数
    8. return pow(fac(a, (n - 1) / 2), 2) * a;
    9. else
    10. return pow(fac(a, n / 2), 2);
    11. }
    12. int main(){
    13. int a, n;
    14. while (~scanf("%d%d", &a, &n)) cout << "递归求解:" << fac(a, n) << endl;
    15. return 0;
    16. }

    4.代码运行截图:

    5.拓展:

    快速幂原理简述:

      

    下面给出快速幂做法

    1. #include
    2. using namespace std;
    3. #define ll long long
    4. const int mod = 1e9 + 7;
    5. ll quickmi(ll a, ll b){
    6. ll res = 1;
    7. while(b){
    8. if(b & 1) res = res * a % mod;
    9. a = a * a % mod;
    10. b >>= 1;//左移,即b / 2。
    11. }
    12. return res;
    13. }
    14. int main(){
    15. int n,m;
    16. cin >> n >> m;
    17. cout << quickmi(n,m);
    18. return 0;
    19. }

     二.求解第K小的数

    1.问题描述:

    给定一个长度为n的无序整数数列,以及一个整数k,求出数列从小到大排序后的第k个数(即求解第K小的数)。 

    2.问题分析:

    基于分治中快排的思想,从数组a[]中找出一个基准值v,把数组分为两部分a[l...j]和a[j+1...r]。a[l...j]中的元素小于v,a[j+1...r]中元素大于v。这时有两种情况:

    a[l...j]中元素的个数大于等于k,则递归到数组a[l...j]中搜索的第k小的数。
    a[l...j]中元素的个数小于k,则递归到数组a[j+1...r]中第k-(j-l+1)小的数
    因为每次分区完只需要继续操作一边,所以该算法的平均时间复杂度是O ( n ) 

    3.Code:

    1. #include
    2. using namespace std;
    3. const int N = 1e5 + 10;
    4. int a[N];
    5. int find(int l, int r, int k){
    6. if(l >= r) return a[l];//两指针相遇就返回
    7. int i = l - 1, j = r + 1;//防溢出
    8. int v = a[l + r >> 1];
    9. //把数组分为两部分a[l...j]和a[j + 1...r],其中a[l...j]中的元素小于v,a[j + 1...r]中元素大于v
    10. while(i < j){//由于内部的while遇到一次不满足的即停止,因此需要外面套一个大while保证整个过程的正常进行
    11. do i ++; while(a[i] < v);
    12. do j --; while(a[j] > v);
    13. if(i < j) swap(a[i], a[j]);
    14. }
    15. //选择区间后进行递归
    16. if(j - l + 1 >= k) return find(l, j, k);
    17. else return find(j + 1, r, k - (j - l + 1));
    18. }
    19. int main(){
    20. int n, k;
    21. cin >> n >> k;
    22. for(int i = 0; i < n; i ++) cin >> a[i];
    23. cout << find(0, n - 1, k) << endl;
    24. return 0;
    25. }

    ps:本题解巧妙之处:不同于两种朴素快排写法,对于快排的实现中i与j两指针可以交错,不影响功能的实现。 

    4.代码运行截图:

     三.如何降低分治法的时间复杂度

    1.分治中的平衡原则:一般情况下,子问题规模几乎相等时,分治法效率较高。

    2.分治可进行二分,三分等等,具体怎么分,需看问题的性质和分治后的效果。只有认真分析分治后可能产生的预期效率,才能灵活地运用分治思想解决实际问题。        

    3.在优化分治方面,通过减少子问题个数,减少子问题规模实现。 减少子问题个数意味着减少分治次数。减少子问题规模意味着提高每个子问题处理效率,均可降低分治法的时间复杂度。

  • 相关阅读:
    一种4g扫码付费通电控制器方案
    基于Win11、CentOS7、VMWare15pro搭建Hadoop2.7.7
    接口测试这件小事
    MySQL安装validate_password_policy插件
    计算机组成原理学习的目的是什么
    无法将“pip”项识别为 cmdlet、函数、脚本文件或可运行程序的名称
    零数受邀在电动汽车百人会2018年会发表演讲
    linux内核的块设备驱动框架详解
    自媒体工作内容管理助手
    echarts图表设置x轴y轴均随滚轮滚动缩+放 区域缩放
  • 原文地址:https://blog.csdn.net/m0_65508678/article/details/127858000