简单的分治策略

简单的分治策略

 

分治,是编程中常用的一种策略,例如在归并排序中就有使用。分治策略是一种递归求解问题的方法,在每层的递归中可分为三个步骤:分解(divide)解决(conquer)合并(combine)分解(divide)指的是将问题划分为一些子问题,子问题与原问题具有相同的形式,但规模较之更小。解决(conquer)指的是递归的求解子问题,当问题规模足够小时,直接求解并开始回溯。合并(combine)指的是将子问题得到的解进行合并。在问题规模较大,需要递归求解的时候称之为递归情况(recursive case),子问题规模足够小时,出现“触底”时,就进入了可直接解决的基本情况(base case)

下面分析利用分治策略处理区域内峰值问题的简单例子:求数组的最大子数组之和

假定有一个整型数组Arr[low~high](为了简化过程,使用简单数据类型加以介绍,并且规定该数组中元素数量不低于1个,不大于1000个),现需求出该数组中的一个和最大的子数组以及它的和值。现在我们进行简单分析,要得到该子数组,我们需要知道它在原数组中的边界。原数组可能可以划分出一个或一个以上的子数组,而和最大便成了我们找寻的依据。

       对问题进行分解,假定Arr[i~j]是我们要求的数组,那么原问题可以分成规模更小的问题,完全位于子数组Arr[low~mid]完全位于子数组Arr[mid+1~high] ,跨越中点mid(mid的值为(low+high)/2)。得到了规模更小的问题后,我们就可以对完全位于子数组Arr[low~mid]完全位于子数组Arr[mid+1~high]这两个规模更小的问题进行递归求解了。然后我们只需要对跨越中点mid这种情况在单独处理下,我们就能够得到每层递归中的子问题的解了,然后再对子问题的解合并,当然这里并不是意味着相加,而是依据我们的条件选择和处理。

       现在对子问题进行处理流程分析。先从特殊的情况开始:

              1、计算low到mid范围内的和值,记为左最大和值。

              2、计算mid+1到high范围内的和值,记为右最大和值。

3、计算左最大和右最大的和,记为跨越中点最大和值。

       然后是完全位于子数组Arr[low~mid]完全位于子数组Arr[mid+1~high]这两种情况,实际上这是处理上等同的情况,因为我们只需要同原问题一样分解处理直到触底发生。所以我们需要:

1、 先判断触底是否发生,触底情况为low和high相等,此时只有一个元素,和值就为该元素本身。

2、 获取左最大

3、 获取右最大

4、 获取跨越中点最大

5、 合并处理三个最大,得到符合条件的子数组和它的和值。

 

下面给出该问题的C语言主要源码:


#include<stdio.h>
#include<limits.h>
 
typedefstruct data
{
    int left;
    int right;
    long long int sum;
}Data;
 
voidfindCrossSubArr(long int *arr,int low,int mid,int high,Data *crossResult)
{
      long long int left_sum=INT_MIN;                 // INT_MIN, C标准中定义的最小数
      long long int right_sum=INT_MIN;
      long long int sum=0;
      int I, j;
    
      //左
      for(i=mid;i>=low;i--)
      {
          sum=sum+arr[i];
          if(sum>left_sum)
          {
              left_sum=sum;
              crossResult->left=i;
          }
      }
      //右
      sum=0;
      for(j=mid+1;j<=high;j++)
      {
          sum=sum+arr[j];
          if(sum>right_sum)
          {
              right_sum=sum;
              crossResult->right=j;
          }
      }
      crossResult->sum=left_sum+right_sum;
     
   retutn ;
}
 
voidfindMaxSubArr(long int *arr,int low,int high,Data *result)
{
       Data leftResult,rightResult,crossResult;
       int mid=(low+high)/2; 			//除以2
       // 触底情况处理
       if(low==high)                          	//只有一个元素
       {
         result->left=low;
         result->right=low;
         result->sum=arr[low];
         return;
	}
      	//分解、解决部分
    	findMaxSubArr(arr,low,mid,&leftResult);               	 	//  左
    	findMaxSubArr(arr,mid+1,high,&rightResult);       		// 右
    	findCrossSubArr(arr,low,mid,high,&crossResult);  		// 跨越中点
     	// 合并处理部分
    	// 左
    	if((leftResult.sum)>=(rightResult.sum)&&(leftResult.sum)>=(crossResult.sum))
     	{
         	result->left=leftResult.left;
         	result->right=leftResult.right;
         	result->sum=leftResult.sum;
         	return ;
     	}
    
     	// 右
     	elseif((rightResult.sum)>=(leftResult.sum)&&(rightResult.sum)>=(crossResult.sum))
     	{
         	result->left=rightResult.left;
         	result->right=rightResult.right;
         	result->sum=rightResult.sum;
         	return ;
     	}
     	// 跨越中点
     	else
     	{
         	result->left=crossResult.left;
         	result->right=crossResult.right;
         	result->sum=crossResult.sum;
         	return ;
    	}    
}
 
intmain(void)
{
    	Data result;
       	long int arr[];
      	// 此处添加输入数据代码》》》》
	findMaxSubArr(arr,low,high,&result);
	// 此处添加结果输出代码》》》》
    return 0;
}



评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值