码迷,mamicode.com
首页 > 其他好文 > 详细

Chapter 4 分治策略

时间:2015-01-27 20:11:43      阅读:255      评论:0      收藏:0      [点我收藏+]

标签:

分治策略:

在每层递归中应用如下三个步骤:

分解:将问题划分为一些子问题(子问题的形式与原问题一样,只是规模更小)。

解决:递归地求解子问题,当然,当子问题规模到你满意时就直接求解!

合并:将子问题的解合并为原问题的解

注: 有时会遇到需求解与原问题不完全一样的子问题,将其求解视为合并的一部分(出现此类情况可以思考下有没必要用分治)

4.1 最大子数组问题

即找出一个数组中元素和最大的子数组(对于全正的数组没意义)

最简单的方式是暴利求解,即两个for循环,但该方法的时间复杂度为θ(n2)

分治策略求解:分两种情况,最大子数组位于两侧子数组或者最大子数组跨越了中点

跨越中点的情况单独取出进行判断,code如下:(由于不知道如何对于T实现+,因此未采用泛型。)

        static List<int> MaxCrossingSubarray(List<int> array,int low,int mid,int high )
        {
            int sum=0;
            int leftsum = array[mid],rightsum=array[mid+1]; //用于记录左右两边最大子和
            int maxleft = mid,maxright=mid+1;    //记录最大和时的下标
            for(int i=mid;i>=low;i--)   //左边最大和(必须从mid开始)
            {
                sum+=array[i];
                if(sum>leftsum)
                {
                    leftsum = sum;
                    maxleft = i;
                }
            }
            sum = 0;
            for(int j=mid+1;j<=high;j++)  //右边最大值(必须从mid+1开始)
            {
                sum += array[j];
                if(sum>rightsum)
                {
                    rightsum = sum;
                    maxright = j;
                }
            }
            List<int> max = new List<int> { maxleft, maxright, rightsum + leftsum };
            return max;
        }

 

对整体进行递归分解成更小的情况,直到都变为单个,此时单个便无需判断是否是最小子数组

        static List<int> MaxSubarray(List<int> array,int low,int high)
        {
            int mid;
            List<int> listleft = new List<int>();
            List<int> listright = new List<int>();
            List<int> listcross = new List<int>();
            if (low == high)
                return new List<int>{low, high, array[low]};
            else
            {
                mid = (low + high) / 2;
                listleft = MaxSubarray(array, low, mid);      
                listright = MaxSubarray(array, mid + 1, high);
                listcross = MaxCrossingSubarray(array, low, mid, high);
                if (listleft[2] >= listright[2] && listleft[2] >= listcross[2]) 
                    return listleft;
                else if (listright[2] >= listleft[2] && listright[2] > listcross[2])
                    return listright;
                else
                    return listcross;
            }
        }

 

为了便于理清该递归的执行顺序,绘制了如下图形(其中的数字代表执行顺序,最好自行调试看看具体运行的步骤)

技术分享

---------练习-----------

4.1-2 暴力求解法如下:(主要就是将每一种情况全都试出来后进行比较)

技术分享
        static List<int> MaxSubarray(List<int> array)
        {
            List<int> maxsub = new List<int>();
            int max=array[0],temp=0;   
            int left=0, right=0;                //用于记录最大子数组在元数组中的下标
            for(int i=0;i<array.Count;i++)
            {
                temp = 0; 
                for(int j=i;j<array.Count;j++)  //此处将j从i开始是为了满足全负数时的情况
                {
                    temp += array[j]; 
                    if (temp > max)
                    {
                        left = i;
                        max = temp;
                        right = j;
                    }
                }
            }
            return new List<int> { left, right, max };
        }
View Code

 

4.1-5 线性时间的搜索最大子数组:

核心思想:从左往右,叠加,并记录其中叠加结果最大的,如果叠加到小于0则将其归零,在往后叠加,将叠加结果与之前记录的最大结果进行比较,超过则换!

技术分享
        static int FindMax(List<int> array)
        {
            int sum = 0, maxsum = 0, k = Int32.MinValue;
            for(int i=0;i<array.Count;i++)
            {
                sum += array[i];    
                if (array[i] > k)
                    k = array[i];
                if (sum > maxsum)
                    maxsum = sum;
                else if (sum < 0)  //此处为核心
                    sum = 0;
            }
            if (maxsum == 0)  //为了满足全负数的情况
                return k;
            return maxsum;
        }
View Code

 

注:此方法没返回最大子数组在元数组中的下标

 4.2 矩阵乘法的Strassen算法

矩阵的乘法:技术分享

nxn的矩阵乘法代码如下:

        static int[,] MatrixMultiply(int[,] arr1,int[,]arr2,int n)
        {
            int[,] arr=new int[n,n];
            for (int i = 0; i < n; i++)
                for (int j = 0; j < n; j++)
                {
                    arr[i, j] = 0;
                    for (int k = 0; k < n; k++)
                        arr[i, j] = arr[i, j] + arr1[i, k] * arr2[k, j];
                }
            return arr;
        }

其他两种方法:简单的分治算法和Strassen方法此处先省略

4.3 用代入法求解递归式

步骤

1.猜测解得形式

2.用数学归纳法求出解中的常数,并证明解是正确的

4.4 用递归树方法求解递归式

主要在于根据递归式绘出树图,进行分析,对每一层进行叠加获得总的代价,进行一定的“不精确”假设获得一个较好的猜测解!

4.5 用主方法(master theorem)求解递归式

针对情况: a≥1 and b>1, f(n)是函数

表达式:       T(n)=aT(n/b)+f(n)

Step1:计算出logba

Step2:比较f(n)与nlogba

以下分为三种情况(以下c代表存在一个常数)

  1. f(n)<cnlogba并且要在多项式意义上的小于,即,f(n)<cnlogba-ε 此时T(n)就取决于nlogba所以有T(n)=θ(nlogba)
  2. f(n)>cnlogba并且要在多项式意义上的大于,即f(n)>cnlogba+ε,此时T(n)就取决于f(n),所以有T(n)=θ(f(n))
  3. f(n)=θ(nlogba),则此时 T(n)=θ(nlogba*lgn)

若三种情况均不符合,则不能使用主定理!!!

 

 

Chapter 4 分治策略

标签:

原文地址:http://www.cnblogs.com/Paul-chen/p/4253194.html

(0)
(0)
   
举报
评论 一句话评论(0
登录后才能评论!
© 2014 mamicode.com 版权所有  联系我们:gaon5@hotmail.com
迷上了代码!