434
社区成员




(一)
首先看到这题,因为之前大一上的时候做过类似的题目,所以第一个想到的不是分治的策略,而是在线处理的方法。
其思想就是因为最大子段和必是一个连续的序列,所以只需设置一个前缀总和变量sum=0和一个maxx=INT_MIN,然后从第一个开始遍历,因其需是连续的子段,所以若当前遍历到的这个数加上之前的总和sum要小于当前这个数,那么sum就需更新为当前这个数,否则就sum加上这个数,代码解释如下:
int sum=0,maxx=INT_MIN; //INT_MIN为int类型下的最小值
for(从头到尾遍历序列)
{
if(sum+a[i]<a[i]) //a[i]为当前遍历的序列元素
sum=a[i];
else
sum+=a[i];
}
不难发现,这样子的策略是完全合法的,因为所求子段和是连续的一连串序列,所以若是之前的sum+a[i]<a[i],相当于之前的总和为负数,而我们要求的是最大的字段和,所以可以舍去前面一段的选择而从当前元素作为子段的起始,最后输出sum的值即可。但若只是这样上交上去,我们会发现出现了答案错误的结果,我们之前定义的maxx也没有用到,这是为什么呢?
这是因为当后面的序列元素为负数时,sum仍然会加上当前元素直至遍历完整个序列,例如以下数据:
-1 2 3 -1 -2 -5
对此,我们只需要在每次遍历玩当前元素的操作之后再加上和maxx的比较即可了,最后再输出maxx作为答案即可AC。
int sum=0,maxx=INT_MIN; //INT_MIN为int类型下的最小值
for(从头到尾遍历序列)
{
if(sum+a[i]<a[i]) //a[i]为当前遍历的序列元素
sum=a[i];
else
sum+=a[i];
if(sum>maxx)
maxx=sum;
}
时间复杂度分析:
不难发现,整个过程只用了一个for循环语句,从头遍历至尾,所以时间复杂度为O(n)。
(二)
分治法的策略;
如果使用分治法的话,最大子段和求解情况有三种:(1)[left,mid],即解在序列的左半边; (2)[mid,right],即解在序列的右半边; (3)[left,mid,right],即解中间区域;以上三种情形的最大值,即为所求. 前两种情形符合子问题递归特性,可以使用递归求出。对于第三种情况,则需要单独处理,只需要比较左半边的和与右半边的和以及左边最大值加上右边最大值加上中间连接元素的和即可。
时间复杂度分析:
分解子问题时间复杂度为O(1),求解子问题时间复杂度为2T(n/2),合并子问题算法时间复杂度为O(1)。
根据公式可以得到分治算法的时间复杂度为T(n)=2T(n/2)+O(n)=O(nlogn).
可以看到虽然分治的策略没有第一种策略的时间效率高,但是相差的只是一个logn的乘数的差距,在一般情况下,logn的量级不会超过10^2。而且并不是每道题目都能有第一种策略这种的解答方式,所以分治的高效性和普遍性不言而喻。
(三)
对于分治法的体会与思考:
学习了本章之后,其实不难发现其本质就是把原本复杂的大问题不断分解成简单的小问题,然后再去求解小问题最后得到大问题的解。在日常生活中也屡见不鲜,处处都有分治法的鲜活样例,例如小组分工,企业管理,而在计算机中则是具体体现在了一系列的算法之中,例如排序里就有快速排序,归并排序等。使得原本处理复杂的问题效率大大提高,具体就反映在了时间复杂度的O(nlogn)与O(n^2)的区别。