这是我参与 8 月更文挑战的第 4 天,活动详情查看: 8月更文挑战
要求:
1.随机给出一个整数序列(可能有负整数),选出其中连续且 非空的一段使得这段和最大。
2.使用分治法解决。
实验过程:
一、穷举法
#include<iostream>
using namespace std;
const int n=10;
int MUL(int p[]){
int sum=0,max=0;
for(int i=0;i<n;i++){
if(p[i]>0){//定位到序列第一个正数
for(;i<n;i++){
sum+=p[i];
if(sum>max)
max=sum;
}
}
}
return max;
}
int main(){
int s[n];
cout<<"请输入长度为"<<n<<"的序列"<<endl;
for(int i=0;i<n;i++){
cin>>s[i];
}
cout<<"最大字段和为:"<<MUL(s)<<endl;
system("PAUSE");
return 0;
}
时间复杂度为o(n2)
二、分治法
基本思想:
将数组从中间一分为二
(1)递归求得左子段的最大序列和leftsum
(2)递归求得右子段的最大序列和rightsum
(3)考虑最大序列和可能横跨mid,从mid开始,向左相加求得最大总和sl,再从mid向右求得最大总和sr,两者相加所得再与leftsum和rightsum相比较,所得便是最大字段和。
具体代码实现如下:
#include<iostream>
using namespace std;
const int n=10;
int MUL2(int s[],int left,int right){
int sum;
if(left==right)
return s[left];
int mid = (left+right)/2;
int leftsum = MUL2(s,left,mid);//求左边最大值
int rightsum = MUL2(s,mid+1,right);//求右边最大值
//求横跨mid的最大区间和
//求从mid向左相加的区间和
int sl=s[mid];
int lefts = 0;
for(int i=mid;i>=left;i--)
{
lefts += s[i];
if(lefts > sl)
sl=lefts;
}
int sr=s[mid+1];
int rights=0;
for(int i=mid+1;i<=right;i++)
{
rights += s[i];
if (rights > sr)
sr=rights;
}
//分治法三种情况比较,确定整个序列的最大字段和
sum = sl+sr;
if(sum<leftsum)
sum=leftsum;
if(sum<rightsum)
sum=rightsum;
return sum;
}
int main(){
int s[n];
cout<<"请输入长度为"<<n<<"的序列"<<endl;
for(int i=0;i<n;i++){
cin>>s[i];
}
cout<<"最大字段和为:"<<MUL2(s,0,n-1)<<endl;
system("PAUSE");
return 0;
}
时间复杂度为o(nlogn)
三、动态规划
基本思想:
#include<cstdio> //比起cin cout更快一些
#include<algorithm>
using namespace std;
const int maxn=2e5+20;//给数组设计一个很大的值200020
int a[maxn],f[maxn];
int m=-0x3f3f3f3f; //初始化最大值,因为数组中数可能都为负值,所以必须选一个无穷小的数
int main(){
int n;
printf("请输入数组长度:");
scanf("%d",&n);
for(int i=1;i<=n;i++){
scanf("%d",&a[i]);
}
for(int i=1;i<=n;i++){
if(f[i-1]>=0){
f[i]=f[i-1]+a[i];
}
else
f[i]=a[i];
m=max(m,f[i]);
}
printf("%d",m);
system("pause");
return 0;
}