理解了俩种方法:
一
题意:最大连续子串和,并保存下标
思路:分治法
a1,a2,a3…an的最大连续子串和用分治
先将序列分成a1,a2,a3…an/2, 和 an/2+1,an//2+2…an
a[1:n]的最大子段和有三种情况:
1.a[1:n]的最大子段和与a[1:n/2]的最大子段和相同.
2.a[1:n]的最大子段和与a[n/2+1:n]的最大子段和相同.
3.a[1:n]的最大子段和由a[1:n/2],a[n/2+1:n]组成
1和2这两种情况可递归求得,对于情形3,容易看出a[n/2]与a[n/2+1]在最优子序列中(因为最大子段和不在左面,也不在右面,只能在中间段,在中间一定包含a[n/2]与a[n/2+1])
所以情况3就可以 算出 s1 = a[n/2,1]的最大连续子串和,s2 = a[n/2+1,n],s1+s2就是情况3 的最优值
然后判断 左面,右面,中间哪个大就是该串的最大连续子串和
如此分治下去
C++是参考别人的代码:
#include<stdio.h>
int a[100005] = {0};
struct Max
{
int sst;
int eend;
}M[400010];
int iMax_a,iMax_b;
int MaxSubSum(int left,int right,int flag)
{
int sum = 0;
if(left == right)
{
sum = a[left] ;
M[flag].sst = left;
M[flag].eend = left; //printf("%d %d %d\n",M[flag].sst,M[flag].eend,flag);
}
else
{
int mid = (left + right) / 2;
int leftsum = MaxSubSum(left,mid,2*flag);
int rightsum = MaxSubSum(mid+1,right,2*flag+1);
int s1 = -9999999;
int Lsum = 0;
int i;
for(i = mid; i >= left; i--)
{
Lsum += a[i];
if(Lsum > s1)
{
s1 = Lsum;
M[flag].sst = i;
//printf("1%d %d\n",M[flag].sst,flag);
}
}
int s2 = -9999999;
int Rsum = 0;
for(i = mid+1;i <= right; i++)
{
Rsum += a[i];
if(Rsum > s2)
{
s2 = Rsum;
M[flag].eend = i;
}
//printf("%d %d\n",M[flag].eend,flag);
}
sum = s1+s2;
if(sum < leftsum)
{
sum = leftsum;
M[flag].sst = M[2*flag].sst;
M[flag].eend = M[2*flag].eend;
// printf("%d %d\n",M[flag].sst,M[flag].eend);
}
if(sum < rightsum)
{
sum = rightsum;
M[flag].sst = M[2*flag+1].sst;
M[flag].eend = M[2*flag+1].eend;
}
}
return sum;
}
int main()
{
int t,k = 0;
scanf("%d",&t);
while(t--)
{
int i,n;
scanf("%d",&n);
for(i = 1; i <= n; i++)
{
scanf("%d",&a[i]);
}
int len = MaxSubSum(1,n,1);
printf("Case %d:\n",++k);
printf("%d %d %d\n",len,M[1].sst,M[1].eend);
if(t != 0)
printf("\n");
}
return 0;
}
二
下面是自己的C代码,动态规划:
用数组a[]记录序列中的数,对于a[i]只有两种可能
1.为一个序列的首
2.为一个序列的尾
用数组d[i]记录以第i个数结尾的序列的最大和,
则d[i]=max(d[i-1]+a[i],a[i]),d[i-1]+a[i]和a[i]分别对应a[i]的两种情况。
#include<stdio.h>
#include<string.h>
int a[100000+11];
int d[100000+11];
int max(int a,int b)
{
return a>b?a:b;
}
int main()
{
int t,i;
scanf("%d",&t);
int k=0;
while(t--)
{
int sum=0;
int n,begin,end;
int max0=-1001; //max0必须小于所有可能的整数
scanf("%d",&n);
memset(a,0,sizeof(a));
memset(d,0,sizeof(d));
for(i = 1 ; i <= n ; i++)
{
scanf("%d",&a[i]);
d[i]=max(d[i-1]+a[i],a[i]);
if(max0 < d[i])
{
max0=d[i]; //记录序列和的最大值
end=i; //记录和最大的序列的尾
}
}
for(i = end ; i >= 1 ; i--)
{
sum+=a[i];
if(sum == max0)
{
begin=i;
}
}
printf("Case %d:\n",++k);
printf("%d %d %d\n",max0,begin,end);
if(t)
{
printf("\n"); //注意输出格式
}
}
}