분할 및 정복 : 가장 큰 하위 배열 문제
질문 :
배열이 주어지면 하위 배열 요소의 합이 가장 큰 하위 배열 (연속)을 찾습니다.
입력 :
배열 X [1 ... n]이 주어지면 배열 첨자가 l, r (l≤r) 인 비어 있지 않은 하위 배열 쌍에 대해 합계는 S (l, s)로 기록됩니다.
출력 :
Find S (l, s)의 최대 값은 S max 로 기록됩니다 .
문제 분석
- 배열 X [1… n]을 X [1… n / 2] 및 X [n / 2 + 1… n]으로 나눕니다.
- 하위 문제를 재귀 적으로 풀기
S1 : 배열의 가장 큰 하위 배열 X [1… n / 2]
S2 : 배열의 가장 큰 하위 배열 X [n / 2 + 1… n] - 하위 문제를 결합하여 S max
S3 : 중간 지점에서 가장 큰 하위
배열 X S max = max {S1, sz, s3} 에서 가장 큰 하위 배열의 합계
실행 효율성의 병목 가치는 S3 해결의 문제인 병합 측면에서 발생합니다.
S3 솔루션 :
-
记 mid = n / 2
-
S3는 두 부분으로 나눌 수 있습니다.
Left : X [mid]로 끝나는 가장 큰 하위 배열의 합
오른쪽 : X [mid + 1]로 시작하는 가장 큰 하위 배열 의 합
S3 = Left + Right왼쪽의 해 :
X [mid]에서 앞쪽으로 합계를 이동하여 최대 값을 기록합니다
. 오른쪽의 솔루션 :
X [mid +1]에서 합계를 뒤로 이동하여 최대 값을 기록합니다.
S3 시간 복잡성 :
- 남은 시간 복잡도 : O (중간)
- 적시 복잡성 : O (n-mid)
- S3 시간 복잡도 : O (n)
알고리즘 분석 다이어그램
암호
#include<iostream>
#include<cstdio>
#include<algorithm>
using namespace std;
const int N = 10000;
int q[N]; // 序列
// 求跨越中间的最大子序列
int findMidSum(int l, int mid, int r) {
int leftSum = -100000, rightSum = -100000; // 初始化为 -100000 是为了防止出现负数和的情况
int sum = 0; // sum 即向左和向右最大的子序列的和
for (int i=mid; i>=l; i--) {
sum += q[i];
// 从mid开始向左加不断更新最大值,最后计算出来的即mid左边最大的子序列的值
leftSum = max(leftSum, sum);
// max()函数为 #include<algorithm>中的方法,可以比较两个数的大小,返回较大的数
}
sum = 0;
for (int i=mid+1; i<=r; i++) {
sum += q[i];
// 从mid开始向右加不断更新最大值,最后计算出来的即mid右边最大的子序列的值
rightSum = max(rightSum, sum);
}
return leftSum + rightSum;
}
// 分治求左右两端最大的子序列和并将其与跨越中间的子序列的和比较大小,返回最大连续子序列的和
int maxSubArr(int l, int r) {
if (l == r) return q[l]; // 递归出口
int mid = l + r >> 1; //右移一位,相当于除2;也可以写成 mid = l+(r-l)/2;
int leftSum = maxSubArr(l, mid);
int rightSum = maxSubArr(mid+1, r);
int midMaxSum = findMidSum(l, mid, r);
int res = max(leftSum, rightSum);
res = max(res, midMaxSum);
// 返回最大的子序列和
return res;
}
int main() {
int n;
cin >> n;
for (int i=0; i<n; i++) scanf("%d", &q[i]);
int res = -100000;
res = maxSubArr(0, n-1);
cout << res;
return 0;
}