分治法求解最大子列和问题

一、什么是分治法
1、分治法简介
2、分治法的设计思想
3、分治法能解决的问题的特征
二、例题详解
最大子列和问题

一、什么是分治法

1、分治法简介

分治法可以通俗的解释为:把一片领土分解,分解为若干块小部 分,然后一块块地占领征服,被分解的可以是不同的政治派别或是其他什么,然后让他们彼此异化。
分治法的精髓:
分–将问题分解为规模更小的子问题;
治–将这些规模更小的子问题逐个击破;
合–将已解决的子问题合并,最终得出“母”问题的解;

2、分治法的思想

分治法的设计思想是,将一个难以直接解决的大问题,分割成一些规模较小的相同问题,以便各个击破,分而治之。
对于一个规模为n的问题,若该问题可以容易地解,比如说规模n较小)则直接解决,否则将其分解为k个规模较小的子问题,这些子问题互相独立且与原问题形式相同,递归地解这些子问题,然后将各子问题的解合并得到原问题的解。

3、分治法能解决的问题的特征

分治法所能解决的问题一般具有以下几个特征:
(1) 该问题的规模缩小到一定的程度就可以容易地解决
(2) 该问题可以分解为若干个规模较小的相同问题,即该问题具有最优子结构性质。
(3) 利用该问题分解出的子问题的解可以合并为该问题的解;
(4) 该问题所分解出的各个子问题是相互独立的,即子问题之间不包含公共的子问题。

二、例题

题目:

给定K个整数组成的序列{ N1, N2.,… Nk},“连续子列” 被定义为{ Ni, Ni+1…Nj},其中1≤i≤j≤K。”最大子列和” 则被定义为所有连续子列元素的和中最大者。例如给定序列{-2, 11,-4, 13, -5,-2} ,其连续子列{ 11,-4, 13 }有最大的和20。现要求你编写程序,计算给定整数序列的最大子列和。
本题旨在测试各种不同的算法在各种数据情况下的表现。各组测试数据特点如下
●数据1 :与样例等价,测试基本正确性;●数据2: 102个随机整数;
●数据3: 103个随机整数;
●数据4: 104个随机整数;
●数据5 : 105个随机整数;
输入格式
输入第一行给出整数K(K<=100000);第二行给出K个整数,其间以空格分隔。
输出格式
在一行中输出最大子列和,如果序列中所有整数都为负数,则输出0。

输入样例

6
-2 11 -4 13 -5 -2

输出样例

20

题目分析
将K个元素分为两部分,mid为中点,左边部分为(left,mid),右边部分为(mid+1,right)。递归求出这两部分的最大子列和。当算法涉及递归的时候,特别需要注意的是递归出口,在这里,递归的出口就是当只有一个元素的时候,若该值大于0,返回该值,小于等于0则返回0。而最大子列和可能出现的情况有三种,第一种在left到mid这个范围中,第二中在mid+1到right这个范围中,第三种就是跨界情况,假设范围为(i,j),其中i<=mid<=j,所以最大子列和取三者的最大值。
代码

#include<iostream>
#include<algorithm>
using namespace std;
int a[100001];
int maxsub(int left,int right){
	//递归出口 ,当只有一个元素的时候,大于0的将其返回,否则返回0 
	if(left==right)
	{
		if(a[left]>0)
		return a[left];
		else 
		return 0;
	}
	int mid=(left+right)/2;//找到中点 
	
	//分的过程 
	int maxLeft =   maxsub(left,mid);
	int maxRight = maxsub(mid+1,right);
	
    //求跨界,该子列包括 a[mid],所以由mid为中心向两边求跨界的最大子列和 
	int borderLeft=0;
	int borderRight=0;
	int sumLeft =0;
	int sumRight=0;
	
	//由中点mid向左扫描 
	for(int i=mid;i>=left;i--){
		borderLeft+=a[i];
		if(borderLeft>sumLeft)//向左更新左边最大子列和 
		sumLeft=borderLeft; 
	} 
	//由中点mid向右扫描
	for(int i=mid+1;i<=right;i++){
		borderRight+=a[i];
		if(borderRight>sumRight)//向右更新右边的最大子列和 
		sumRight=borderRight;
	} 
	// sumRight+sumLeft为该范围跨界的最大子列和
	//maxLeft为left到mid范围内的最大子列和
	//maxRight为mid+1到right的最大子列和 
	//返回三者的最大值 
	return max(max(maxLeft,maxRight),sumRight+sumLeft);
}
int main(){
	int n;
	cin>>n;
	for(int i=0;i<n;i++){
		cin>>a[i];
	}
	int sum = maxsub(0,n-1);
	cout<<sum<<endl;
}
发布了5 篇原创文章 · 获赞 12 · 访问量 408

猜你喜欢

转载自blog.csdn.net/weixin_44736475/article/details/105091927
今日推荐