class Solution {
public:
int maxSubArray(int A[], int n) {
int res = INT_MIN;
int tem = 0;
for(int i = 0; i < n; ++i)
{
tem = max(A[i],tem + A[i]);
res = max(res,tem);
}
return res;
}
};
状态转移方程:
dp[i] = max(a[i],dp[i-1] + a[i])
记住一个局部最大和一个全局最大就可以了
为了能够找到 起始和终止位置 加一个记录就可以了
用这个改的代码通过了杭电的这个题目 应该没问题的
#include <iostream>
#include <cstdio>
using namespace std;
class Solution {
public:
Solution()
{
start = end = 0;
}
int start,end;
int maxSubArray(int A[], int n) {
int res = INT_MIN;
int tem = 0;
int start_fake = 0,end_fake = 0;
for(int i = 0; i < n; ++i)
{
//tem = max(A[i],tem + A[i]);
// res = max(res,tem);
if(A[i] <= tem + A[i])
{
tem = tem +A[i];
end_fake = i;
}
else
{
tem = A[i];
start_fake = i;
end_fake = i;
}
if(res <= tem)
{
res = tem;
start = start_fake;
end = end_fake;
}
}
return res;
}
};
int main()
{
// freopen("in.txt","r",stdin);
// int a[9] = {-2,1,-3,4,-1,2,1,-5,4};
// Solution s;
// cout<<s.maxSubArray(a,9)<<endl;
// cout<<s.start<<endl<<s.end<<endl;
int T;
int a[100000];
cin>>T;
for(int i = 1;i <= T ;++i)
{
int N;
Solution s;
cin>>N;
for(int j = 0;j < N ;++j)
{
cin>>a[j];
}
cout<<"Case "<<i<<":"<<endl;
cout<<s.maxSubArray(a,N)<<" ";
cout<<s.start + 1<<" ";
cout<<s.end + 1<<endl;;
if(i != T)cout<<endl;
}
return 0;
}
这个是以前ac这题的代码
思想差不多
#include <iostream>
using namespace std;
int main()
{
int T,N;
int sum,max;
int a,start,end;
cin>>T;
for(int i = 1;i <= T;i++)
{
cin>>N;
start = end =1;
sum = 0,max = -9999;
for(int j = 1,k = 1;j <=N;j++)
{
cin>>a;
sum += a;
if(sum > max){max = sum; start = k;end = j;}
if(sum < 0){sum = 0;k = j + 1;}
}
cout<<"Case "<<i<<":"<<endl<<max<<" "<<start<<" "<<end<<endl;
if(i < T)cout<<endl;
}
return 0;
}
今天又看到一个分治的方法
思路:分治
如果将所给数组(A[0],...,A[n-1])分为长度相等的两段数组(A[0],...,A[n/2-1])和(A[n/2],...,A[n-1]),分别求出这两段数组各自最大子段和,
则原数组(A[0],...,A[n-1])的最大子段和分为以下三种情况,要么在前半部分a中,要么在后半部分b中,要么跨越a和b之间的边界:
a.(A[0],...,A[n-1])的最大子段和与(A[0],...,A[n/2-1])的最大子段和相同;
b.(A[0],...,A[n-1])的最大子段和与(A[n/2],...,A[n-1])的最大子段和相同;
c.(A[0],...,A[n-1])的最大子段跨过其中间两个元素A[n/2-1]到A[n/2];
对应a和b两个问题是规模减半的两个相同的子问题,可以用递归求得。
对于c,需要找到以A[n/2-1]结尾的最大的一段连续数组之和S1=(A[i],...,A[n/2-1])和以A[n/2]开始的最大的一段连续数组之和S2=(A[n/2],...,A[j]),那么第三种情况的最大值为S1+S2。
只需要对原数组进行一次遍历即可。在a中的部分是a中包含右边界的最大子数组,在b中的部分是b中包含左边界的最大子数组。
这其实是一种分治策略,时间复杂度为O(nlogn)。
http://ask.julyedu.com/question/129
http://blog.csdn.net/sunnyyoona/article/details/43200983
模仿着写了一遍 也很好理解
class Solution {
public:
int maxSubArray(int A[], int n) {
return divide(A,0,n-1);
}
int divide(int A[],int left,int right)
{
if(left > right)return 0;
if(left == right)return A[left];//递归出口
//先把有交叉的两部分算出来
int mid = (right - left) / 2 + left;
int sum = 0,left_max = INT_MIN;
for(int i = mid;i>=left;i--)
{
sum += A[i] ;
left_max = max(sum,left_max);
}
sum = 0;
int right_max = INT_MIN;
for(int i = mid +1;i <= right;i++)
{
sum += A[i];
right_max =max(sum,right_max);
}
int a = divide(A,left,mid);
int b = divide(A,mid+1,right);
int c = left_max + right_max;
return max(max(a,b),c);
}
};