題目链接:http://acm.hdu.edu.cn/showproblem.php?pid=1003
题目大意,给定一个数组,a[0], a[1], ..., a[n-1]需要求它的一个连续子序列,使得这个连续子序列的和最大.
一、暴力求解方法O(n^3)
直观的解法是,遍历所有的连续子序列,取和最大的那个.唯一决定一个连续子序列的指标为序列起始、结束索引,分别设置为i和j则,要求a[i],...,a[j]的和.其中0<=i<n, i<=j<n,如此遍历即可.i和j为两层循环,求和一层循环,共三层循环,复杂度O(n^3),代码如下:
#include <iostream>
using namespace std;
int main()
{
int T;
cin >> T;
while(T--)
{
int n, i, j, k;
cin >> n;
int *a = new int[n];
for(i=0;i<n;i++)
cin >> a[i];
int maxsum = -9999999;
int start = 0, end = 0;
for(i=0;i<n;i++) //Time complexity: O(n^3)
{
for(j=i;j<n;j++) // pay attention this: j starts from i not i+1
{
int sum = 0;
for(k=i;k<=j;k++)
{
sum += a[k];
}
if(sum>maxsum)
{
maxsum = sum;
start = i;
end = j;
}
}
}
cout << maxsum << " " << start+1 << " " << end+1 << endl;
delete [] a;
}
return 0;
}
二、暴力求解方法O(n^2)
比方法一复杂度低的暴力求解方法,主要技巧是:先求出从a[0]开始到a[i]的和S_i(0<=i<n),一次循环. 再计算S_i+1,j = S_j - S_i, 注意下表的范围.由于不用求和,只需要计算两个值的差值,所以只有i和j循环,两层,复杂度O(n^2).
需要注意下标的问题.代码如下:
#include<iostream>
using namespace std;
int main()
{
int T;
cin >> T;
while(T--)
{
int n, i, j;
cin >> n;
int *a = new int[n+1]; //pay attention the index: S_i+1,j = S_j - S_i
int *sum0_i = new int[n+1];
for(i=1;i<=n;i++)
{
cin >> a[i];
sum0_i[i] = 0;
}
sum0_i[0] = 0;
for(i=1;i<=n;i++)
sum0_i[i] = sum0_i[i-1] + a[i];
int maxsum = -99999;
int start, end;
for(i=0;i<=n;i++)
{
for(j=i+1;j<=n;j++)
{
int d = sum0_i[j]-sum0_i[i];
if(d > maxsum)
{
maxsum = d;
start = i+1; // start = i+1 not i !!
end = j;
}
}
}
cout << maxsum << " " << start << " " << end << endl;
delete []a;
}
return 0;
}
三、暴力求解方法O(n^2)
思路和二差不多,主要省去了求和这一层,技巧是:利用前面求和的结果,而不是重新循环一次.代码如下:
#include<iostream>
using namespace std;
int main()
{
int T;
cin >> T;
while(T--)
{
int n, i, j;
cin >> n;
int *a = new int[n];
for(i=0;i<n;i++) cin >> a[i];
int maxsum = -99999;
int start=0, end=0;
for(i=0;i<n;i++) // Time complexity: O(n^2)
{
int sum = 0;
for(j=i;j<n;j++)
{
sum += a[j];
if(sum>maxsum)
{
maxsum = sum;
start = i;
end = j;
}
}
}
cout << maxsum << " " << start+1 << " " << end+1 << endl;
delete []a;
}
return 0;
}
四、分治法O(nlogn)
不懂
五、累积求和的方法
这种方法最重要的一点是:
给定数组,a[0], a[1], ..., a[i], ...,a[j], ..., a[n-1],设S[i,j] = a[i]+a[i+1]+...+a[j]
如果S[i, j]<0, 那么S[i, j] + a[j+1] < a[j+1], 所以连续子序列{a[i], ..., a[j], a[j+1]}这个子序列的和一定是最大的,所以子序列{a[i], ..., a[j]}没有继续增长的必要,需要从a[j+1]开重新增长子序列
(上面仅供思考,不是严谨的证明)
AC的代码如下:
#include<iostream>
using namespace std;
int main()
{
int T;
cin >> T;
int t = 0;
while(T--)
{
t ++;
int n, i, j;
cin >> n;
int *a = new int[n];
for(i=0;i<n;i++)
cin >> a[i];
int maxsum = -9999;
int sum = 0;
int startMax = 0, endMax = -1;
int startTemp = 0, endTemp = -1;
for(i=0;i<n;i++)
{
sum+=a[i];
endTemp ++;
if(sum>maxsum)
{
maxsum = sum;
startMax = startTemp;
endMax = endTemp;
}
if(sum<0)
{
sum = 0;
startTemp = i+1;
endTemp = i;
}
}
cout << "Case " << t << ":" << endl;
cout << maxsum << " " << startMax+1 << " " << endMax+1 << endl;
if(T>0)
cout << endl;
delete []a;
}
return 0;
}
详细的解答参考:
最大连续子序列和:动态规划经典题目(2)