问题描述:
给一串整数下标从1~n,找出两个子串(每个子串中的下标连续),下标分
别为s1~t1和s2~t2,其中1<=s1<=t2<s2<=t2<=n,问子串和最大为多少?
引:
求一串数中一个连续子串和最大值的DP解法为:
设原数组为a[]
令b[j]为以第j个元素结尾的字串和的最大值。只有两种情况:1.只有a[j] 2.a[j]
接在以a[j-1]结尾的字串后面
对于情况二的证明,可用反证法证明得,以a[j-1]结尾的子串和必为该状态的最大值
。
所以b[j]=max{a[j],b[j-1]+a[j]}
这样便求出了以每个元素结尾的字串和最大值,由于实际最大值字串必为其中之一,
再遍历一遍取其中最大值即可。
思路:
有了上述求一个子串最大值的解法,再推到求两个字串最大值的方法就很自然了。只
要从a[0]开始,依次取a[0],a[1].....为划分元素,
即s1<=t1<=a[x]<s2<=t2,并分别求最大值,并再这些最大值中再找最大值
证明如下,假设实际最优划分为s1,t1,s2,t2,t1<s2,可取划分元素为t1,则该最大值
必和以t1为划分元素时两边的最大值的和相等,否则矛盾。因此最优解必为n种划分
情况中的一种的最优解。
问题现在转化为,如何找到每种划分的最大值?其实只要分找两边字串各自的最大字
串和即可(反证法)
解法:
1.从头到尾遍历一遍,求出leftsum[x]
2.从尾到头遍历一遍,求出rightsum[x]
3.从头到尾遍历一遍,求出最大的和
步骤一和二中,要用中间变量b记录中间值。以步骤1为例,要先用b记录以a[i](设
划分元素为a[i])结尾的最大字串和,再与leftsum[i-1]比较,即比较包括a[i]和不
包括a[i]的字串,找出最大值,即为a[0]~a[i]的所有字串的最大值。步骤二同理。
这个算法耗时O(n),是比较快的算法。我刚开始做的时候用了比较笨的方法,就是把
求一个字串最大和做成一个函数,再遍历数组同时调用这个函数来算leftsum和
rightsum。这样需耗时O(n2),因为调用一次函数的代价为O(n),结果超时了。。。
这题还有个小陷阱,就是这个最大和可能是负值,考虑全是负数的情况,但即便如此
算法最多只会算两个负数相加,题目中条件说元素的绝对值<=10000,因此sum的初始
值可赋-20000,保险起见可赋-60000,ok,这下可以AC了~~~
源代码如下:
#include<iostream>
#include<stdio.h>
using namespace std;
int main()
{
int T;
cin>>T;
int t,i,k;
for(t=0;t<T;t++) {
int n,sum=-60000,b; //注意sum可能小于0!!!(全是负数,但此时最多为两
个负数的和)
cin>>n;
int *a=new int[n];
int *lsum=new int[n];
int *rsum=new int[n];
for(i=0;i<=n-1;i++)
scanf("%d",&a[i]);
lsum[0]=a[0];
rsum[n-1]=a[n-1];
b=a[0];
for(k=1;k<=n-1;k++) {
if(b>0) b+=a[k];
else b=a[k];
if(b>lsum[k-1]) lsum[k]=b;
else lsum[k]=lsum[k-1];
}
b=a[n-1];
for(k=n-2;k>=0;k--) {
if(b>0) b+=a[k];
else b=a[k];
if(b>rsum[k+1]) rsum[k]=b;
else rsum[k]=rsum[k+1];
}
for(k=0;k<=n-2;k++)
if(lsum[k]+rsum[k+1]>sum) sum=lsum[k]+rsum[k+1];
delete[] a;
delete[] lsum;
delete[] rsum;
cout<<sum<<endl;
}
return 0;
}
PS:最好养成new过delete的习惯,我比较了下,一个耗内存800+K,一个12000+K,数据
再多些就要爆内存了。。
还有输入比较多时用scanf划算,省时呵呵。
本文介绍了一种高效算法,用于找出一串整数中两个连续子串的最大和,通过动态规划思想实现O(n)时间复杂度。文章详细解释了解决方案的思路和步骤,并提供了完整的C++代码实现。
621

被折叠的 条评论
为什么被折叠?



