Dividir para conquistar: o maior problema de subarray
Pergunta:
Dada uma matriz, encontre uma submatriz (contígua) de forma que a soma dos elementos da submatriz seja a maior.
Entrada:
dada uma matriz X [1 ... n], para qualquer par de submatrizes não vazias com subscritos de matriz l, r (l≤r), a soma é registrada como S (l, s).
Saída:
Encontre S O valor máximo de (l, s) é registrado como S max .
analise de problemas
- Divida a matriz X [1 ... n] em X [1 ... n / 2] e X [n / 2 + 1 ... n]
- Resolva recursivamente o subproblema
S1 : a maior submatriz da matriz X [1 ... n / 2]
S2 : a maior submatriz da matriz X [n / 2 + 1 ... n] - Combine os subproblemas para obter S max
S3 : a maior submatriz do ponto médio. A
soma das submatrizes mais principescas da matriz X S max = max {S1, sz, s3}
O valor de gargalo da eficiência de execução está no aspecto da fusão, que é o problema de resolver S3.
Solução S3:
-
记 mid = n / 2
-
S3 pode ser dividido em duas partes
Esquerda : a soma da maior submatriz terminando com X [mid]
Direita : a soma da maior submatriz começando com X [mid + 1]
S3 = Esquerda + DireitaSolução da esquerda:
percorra a soma de X [meio] para a frente e registre o valor máximo
. Solução da direita:
percorra a soma de X [meio +1] para trás e registre o valor máximo
Complexidade de tempo S3:
- Complexidade do tempo restante: O (meio)
- Complexidade de tempo certo: O (n-mid)
- Complexidade de tempo S3: O (n)
Diagrama de análise de algoritmo
Código
#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;
}