您的位置:首页 > 编程语言

编程之美2.14 求子数组和的最大值(首尾相连)

2015-04-20 22:15 381 查看
整体思路:
问题的解可以分为两种情况:

1)解没有跨过A[n-1]到A[0],即普通的求子数组和的最大值

2)解跨过A[n-1]到A[0]

对第二种情况,只要找到从A[0]开始求和的最大的一段(A[0],...,A[j])(0<=j<n)以及A[n-1]结尾的求和的最大的一段(A[i],...,A[n-1])(0<=i<n),(此时两段有可能重合,所以在求各段最值是要分别记下下标i,j,见下面代码)
那么第2种情况中,和的最大值M_2为:

M_2=A[i]+...+A[n-1] + A[0] +...+ A[j]

如果i<=j,则
M_2是整个数组A[0]...A[n-1]的和再减去A[0]...A[n-1]中子数组和为负数的最小值
//(编程之美上说i<=j时M_2=A[0]+...+A[n-1]本以为是正确的,做一下才知道其实不对.如:1
-2 3 5 -3 2.)

否则

M_2=A[0]+...+A[j] + A[i]+...+A[n-1]

#include<iostream>
using namespace std;

int max(int a, int b)

{

return (a > b) ? a : b;

}

//从尾到首动态规划

int MaxSum(int *A, int length)

{

//先求出A[0],A[1],...A[n-1](首尾不连接)的情况下子数组和最大值nAll

/*int nStart = A[length - 1];

int nAll = A[length - 1];

for (int i = length - 2; i >= 0; i--){

nStart = max(A[i], A[i] + nStart);

nAll = max(nStart, nAll);*/

int nAll = A[0],sum=0;

for (int i = 0; i < length ;i++)

{

sum += A[i];

if (sum>nAll)

nAll = sum;

if (sum < 0)

sum = 0;

}

//下面处理整体思路的第二种情况,即跨过A[n-1],A[0]

//先求A[n-1]结尾的和最大的一段(A[i],...,A[n-1])(0<=i<n)

sum = 0;

int ltempmax = A[length - 1];

int lpos = length-1;

for (int i = length - 1; i >= 0; i--)

{

sum += A[i];

if (sum>ltempmax)

{

ltempmax = sum;

lpos = i;

}

}

//求A[0]开始和最大的一段(A[0],...,A[j])(0<=j<n)

sum = 0;

int rtempmax = A[0];

int rpos = 0;

for (int i = 0; i<length; i++)

{

sum += A[i];

if (sum>rtempmax)

{

rtempmax = sum;

rpos = i;

}

}

//如果lpos<=rpos,则循环数组中可能出现的子数组最大值要么是A[0]...A[n-1]子数组和的最大值nAll

//要么是整个数组A[0]...A[n-1]的和再减去A[0]...A[n-1]中子数组和为负数的最小值

if (lpos <= rpos)
{

//求数组中和为负数且的最小值

int sum = 0;

int minAll = A[0];

for (int i = 0; i<length; i++)

{

sum += A[i];

if (sum < minAll)

minAll = sum;

if (sum>0)

sum = 0;

}

int tempmax = 0;

for (int i = 0; i < length; i++)

tempmax += A[i];

//比较A[0]...A[n-1]子数组和的最大值nAll跟A[0]...A[n-1]的和再减去A[0]...A[n-1]中子数组和为负数的最小值

return max(nAll, tempmax - minAll);

}

else

{

//比较A[0]+...+A[j] + A[i]+...+A[n-1]即ltempmax+rtempmax的值跟A[0]...A[n-1]子数组和的最大值nAll

return max(nAll, ltempmax + rtempmax);

}

}

int main(){

int a[6] = { 1, -2, 3, 5, -3, 2 };

int b[6] = { 1, -2, 3, 5, -1, 2 };

int c[6] = { 2, -1, 3, 5, -2, 1 };

int d[6] = { -9, -2, -3, -5, -3 };

int e[3] = { 3, -2, 3 };

cout << "a 的子数组和的最大值是(正确结果应该返回9) " << MaxSum(a, 6) << endl;

cout << "b 的子数组和的最大值是(正确结果应该返回10) " << MaxSum(b, 6) << endl;

cout << "c 的子数组和的最大值是(正确结果应该返回10) " << MaxSum(c, 6) << endl;

cout << "d 的子数组和的最大值是(正确结果应该返回-2) " << MaxSum(d, 5) << endl;

cout << "e 的子数组和的最大值是(正确结果应该返回6) " << MaxSum(e, 3) << endl;

system("pause");

return 0;

}

其实第二种情况是不是可以统一下为:
M_2=A[0]+A[1]+...+A[n-1]-子数组和为负的最小值.(但是当数组全为负时,如d,第一情况可以求出负数中的最大值-2,但是第二种情况求出的结果为0,一总和,结果为0.所以,可以求max是做下判断
int max(int M_1,int M_2)
{
if(M_2==0)
return M_1;
else
return M_1>M_2?M_1:M_2;
}
所以改进代码如下:
#include<iostream>

using namespace std;

int max(int a, int b)

{

if (b == 0)

return a;

else

return (a > b) ? a : b;

}

int MaxSum(int *A, int length)

{

//先求出A[0],A[1],...A[n-1](首尾不连接)的情况下子数组和最大值M_1

int M_1 = A[0], sum = 0;

for (int i = 0; i < length; i++)

{

sum += A[i];

if (sum>M_1)

M_1 = sum;

if (sum < 0)

sum = 0;

}

//再求出A[0],A[1],...A[n-1](首尾连接)的情况下子数组和最大值M_2

int arraysum = 0;

for (int i = 0; i < length; i++)

arraysum += A[i];

//求数组中和为负数且的最小值

sum = 0;

int minAll = A[0];

for (int i = 0; i<length; i++)

{

sum += A[i];

if (sum < minAll)

minAll = sum;

if (sum>0)

sum = 0;

}

int M_2 = arraysum - minAll;

return max(M_1, M_2);
//比较A[0]...A[n-1]子数组和的最大值nAll跟A[0]...A[n-1]的和再减去A[0]...A[n-1]中子数组和为负数的最小值

}

int main(){

int a[6] = { 1, -2, 3, 5, -3, 2 };

int b[6] = { 1, -2, 3, 5, -1, 2 };

int c[6] = { 2, -1, 3, 5, -2, 1 };

int d[6] = { -9, -2, -3, -5, -3 };

int e[3] = { 3, -2, 3 };

cout << "a 的子数组和的最大值是(正确结果应该返回9) " << MaxSum(a, 6) << endl;

cout << "b 的子数组和的最大值是(正确结果应该返回10) " << MaxSum(b, 6) << endl;

cout << "c 的子数组和的最大值是(正确结果应该返回10) " << MaxSum(c, 6) << endl;

cout << "d 的子数组和的最大值是(正确结果应该返回-2) " << MaxSum(d, 5) << endl;

cout << "e 的子数组和的最大值是(正确结果应该返回6) " << MaxSum(e, 3) << endl;

system("PAUSE");

return 0;

}

内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: