• 分治法思考题


     一.求解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.在优化分治方面,通过减少子问题个数,减少子问题规模实现。 减少子问题个数意味着减少分治次数。减少子问题规模意味着提高每个子问题处理效率,均可降低分治法的时间复杂度。

  • 相关阅读:
    Servlet | HttpServletRequest接口、通过request接口获取请求参数
    计算机网络第二章思考题
    Nginx 无法正确加载静态文件,静态文件加载404或者为html;Nginx 配置访问指定url路径项目部署;
    【操作系统】第三章 —— 如何管理物理内存
    uni-app:多种方法写入图片路径
    Java设计与实现“秒杀”活动之抢粽子【完整版】
    【Docker】镜像的创建、管理与发布
    微服务概述
    聊聊缓存如何进行测试的
    ESP32开发日志记录
  • 原文地址:https://blog.csdn.net/m0_65508678/article/details/127858000