Q:HZ偶尔会拿些专业问题来忽悠那些非计算机专业的同学。今天测试组开完会后,他又发话了:在古老的一维模式识别中,常常需要计算连续子向量的最大和,当向量全为正数的时候,问题很好解决。但是,如果向量中包含负数,是否应该包含某个负数,并期望旁边的正数会弥补它呢?例如:{6,-3,-2,7,-15,1,2,2},连续子向量的最大和为8(从第0个开始,到第3个为止)。给一个数组,返回它的最大连续子序列的和,你会不会被他忽悠住?(子向量的长度至少是1)
T:
1.对于一个数A,若是A的左边累计数非负,那么加上A能使得值不小于A,认为累计值对整体和是有贡献的。如果前几项累计值负数,则认为有害于总和,total记录当前值。
注意:C++中表示正无穷是INF_MAX,负无穷是INF_MIN,需要 #include
int FindGreatestSumOfSubArray(vector array) {
if (array.empty())
return 0;
int max = INF_MIN;
int cur = 0;
for (int i = 0; i < array.size(); i++) {
cur += array[i];
if (cur > max)
max = cur;
if (cur < 0)
cur = 0;
}
return max;
}
2.动态规划法:遍历数组,定义一个变量来存储当前向量子序列最大值,\(F(i)=max(F(i-1)+array[i] , array[i])\)。
int FindGreatestSumOfSubArray(vector array) {
if (array.empty())
return 0;
int result = array[0];
int cur = array[0];
for (int i = 1; i < array.size(); i++) {
cur = max(cur + array[i], array[i]);
result = max(result, cur);
}
return result;
}
3.分治法:
考虑将数组从中间分为两个子数组,则最大的比出现在三种情况之一
- 完全位于左边数组
- 完全位于右边数组
- 跨越中点,包含左右数组靠近中点的部分
具体实现: 递归左右子数组再分为两个数组,直到子数组只有一个元素
int FindGreatestSumOfSubArray(vector array) {
if (array.empty())
return 0;
return Find(array, 0, array.size() - 1);
}
int Find(vector array, int left, int right) {
if (left == right) {
return array[left];
}
int mid = (left + right) / 2;
//跨越中点,包含左右数组靠近中点的部分
int maxLeftm = INT_MIN;
int temp = 0;
for (int i = mid; i >= left; i--) {
temp = temp + array[i];
maxLeftm = max(maxLeftm, temp);
}
int maxRightm = INT_MIN;
temp = 0;
for (int i = mid + 1; i <= right; i++) {
temp = temp + array[i];
maxRightm = max(maxRightm, temp);
}
int maxMid = maxLeftm + maxRightm;
//完全位于左边数组
int maxLeft = Find(array, left, mid);
//完全位于右边数组
int maxRight = Find(array, mid + 1, right);
return max(maxRight, max(maxMid, maxLeft));
}