P1440 求m区间内的最小值 【单调队列】

利用单调队列解决一个数列中每个元素前m个数的最小值问题,当区间不足m项时从第1项开始,若无前导数则输出0。给定n和m,以及数列a,输出每个ai前m个数的最小值。对于所有数据,1≤m≤n≤2×10^6,1≤ai≤3×10^7。使用STL或手写代码实现。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

题目描述

一个含有 nnn 项的数列,求出每一项前的 mmm 个数到它这个区间内的最小值。若前面的数不足 mmm 项则从第 111 个数开始,若前面没有数则输出 000。
输入格式

第一行两个整数,分别表示 nnn,mmm。

第二行,nnn 个正整数,为所给定的数列 aia_iai​。
输出格式

nnn 行,每行一个整数,第 iii 个数为序列中 aia_iai​ 之前 mmm 个数的最小值。
输入输出样例
输入 #1

6 2
7 8 1 4 3 2

输出 #1

0
7
7
1
1
3

说明/提示

对于 100%100%100% 的数据,保证 1≤m≤n≤2×1061\le m\le n\le2\times10^61≤m≤n≤2×106,1≤ai≤3×1071\le a_i\le3\times10^71≤ai​≤3×107。

这题若用STL来写但不用快读的话会T。

手写版

#include <iostream>
#include <algorithm>
#include <queue>
#include <cstdio>
using namespace std;

const int maxn = 2e6 + 10;
int id[maxn];
int data[maxn];
int a[maxn];
int head, tail;
int n, m;

int main() {
	scanf("%d%d", &n, &m);
	for(int i = 1; i <= n; i++)	scanf("%d", &a[i]);
	for(int i = 1; i <= n; i++) {
		while(head < tail && id[head] < i - m)	head++;
		//head == tail ? cout << 0 << endl : cout << data[head] << endl;
		printf("%d\n", data[head]);
		while(head < tail && data[tail - 1] > a[i])	tail--;
		data[tail] = a[i];
		id[tail] = i;
		tail++;
	}
	return 0;
}

stl版

#include <iostream>
#include <algorithm>
#include <queue>
#include <cstdio>
using namespace std;

const int maxn = 2e6 + 10;
struct node{
	int data;
	int id;
}a[maxn];

inline int rd()
{
    int data = 0;
    int f = 1;
    char ch = getchar();
    while(ch < '0'||ch > '9')
    {
        if(ch == '-')
            f = -1;
        ch = getchar();
    }
    while(ch >= '0'&&ch <= '9')
    {
        data = (data<<3) + (data<<1) + ch - '0';
        ch = getchar();
    }
    return f * data; 
}

int main() {
	int n, m;
//	ios::sync_with_stdio(false);
//	cin >> n >> m;
//	scanf("%d%d", &n, &m);	
	n = rd(), m = rd();
	for(int i = 1; i <= n;i++)
		a[i].data = rd(), a[i].id = i;
	deque<node>que;
	for(int i = 1; i <= n; i++) {
		while(!que.empty() && que.front().id < i - m)	que.pop_front();
//		que.empty() ? cout << 0 << endl : cout << que.front().data << endl;
		que.empty() ? printf("0\n") : printf("%d\n", que.front().data);
		while(!que.empty() && que.back().data >= a[i].data)	que.pop_back();
		que.push_back(a[i]);
	}
	return 0;
}
<think>嗯,我需要解决这个问题:计算给定区间的最大子数组和,但子数组的长度必须在P到Q之间。用户提到的可能方法包括缀和数组和类似动态规划的方法。首先,我得理清楚问题的具体要。 首先,最大子数组和通常可以用Kadane算法,但这里有限制条件,子数组的长度必须在P到Q之间。这意味着传统的Kadane算法可能不太适用,因为它在每一步只考虑当元素是否加入子数组,而不考虑长度限制。所以需要考虑其他方法。 根据引用[2]中的内容,缀和数组可以帮助快速计算区间和。比如,缀和数组P[i]表示原数组i个元素的和,这样区间[i,j]的和就是P[j] - P[i-1]。这可能有用,因为我们需要计算多个不同长度区间的和,并找到最大值。 但问题是如何在长度限制下找到最大和。可能的思路是遍历所有可能的子数组,其长度在P到Q之间,然后计算它们的和,找到最大的那个。但是直接遍历的话,时间复杂度可能会很高,比如数组长度为n,每个可能的起始点i,检查从i到i+P-1到i+Q-1的子数组,这样的复杂度大概是O(n*(Q-P)),但如果Q接近n的话,这会是O(n²),可能不够高效。尤其是当n很大时,比如超过1e5,这样的方法会超时。 所以需要考虑优化。比如,使用滑动窗口或者结合缀和的方式。 假设使用缀和数组pre_sum,其中pre_sum[i]表示i个元素的和,那么区间[i, j]的和是pre_sum[j] - pre_sum[i-1]。要找长度在L到Q之间的区间的最大和,可以遍历每个可能的右端点j,然后找到在j-Q到j-P之间的左端点i,使得pre_sum[j] - pre_sum[i]最大。这样,对于每个j,我们需要在i的范围[j-Q, j-P]中找到最小的pre_sum[i],这样pre_sum[j] - pre_sum[i]就会最大。这时候可以使用单调队列来维护这个范围内的最小值,从而将时间复杂度降低到O(n)。 但需要确认区间的长度是否在P到Q之间。例如,当右端点j固定时,左端点i必须满足j - i >= P 且 j - i <= Q。即i <= j - P 且 i >= j - Q。所以对于每个j,左端点i的范围是max(0, j-Q) <= i <= j-P。比如,当j=5,Q=3,P=2时,i的范围是5-3=2到5-2=3,即i=2到3。这可能吗?或者可能我需要重新考虑范围。 或者,区间长度是j - i + 1。例如,假设子数组从i到j,包含i和j,则长度是j - i + 1。所以当长度在P到Q之间时,j - i +1 >= P → i <= j - P +1;同时j - i +1 <= Q → i >= j - Q +1。因此,对于每个j,左端点i的可能范围是max(0, j - Q +1) ≤ i ≤ j - P +1。例如,当Q=5,P=2,j=10时,i的范围是10-5+1=6到10-2+1=9,即i=6到9。这时候,对于每个j,需要找到i在这个范围内,使得pre_sum[i-1]最小,这样pre_sum[j] - pre_sum[i-1]最大。 这样的话,我们可以维护一个单调队列,保存可能的i值,使得对应的pre_sum[i-1]是递增的。这样,当处理到j时,队列头部的i对应的pre_sum[i-1]是当范围内的最小值。这样,每次取出队列头部的元素,计算pre_sum[j] - pre_sum[i-1],并比较是否是最大值。 这样的方法的时间复杂度是O(n),因为每个元素最多入队和出队一次。 具体步骤可能如下: 1. 计算缀和数组pre_sum,其中pre_sum[0]=0,pre_sum[1]=arr[0], pre_sum[2]=arr[0]+arr[1],等等。 2. 初始化一个双端队列(deque)来维护i的范围,其中i对应的pre_sum[i-1]是递增的。 3. 遍历每个j,从P到n(因为最小长度是P,所以j至少是P的位置,比如j从P开始到n)。 对于每个j,左端点i的范围是i_start = max(0, j - Q), i_end = j - P。或者需要重新确认这个i的范围是否正确。 或者可能,子数组的长度是k,其中P ≤k ≤ Q。那么对于每个可能的k(即从P到Q),计算所有长度为k的子数组的和,然后找出最大值。这样时间复杂度是O(n*(Q-P+1))。当Q-P的值比较小时,比如是常数,那么这方法是可行的。但如果Q-P很大,比如接近n,这样时间复杂度会很高。 例如,如果数组长度是n=1e5,Q-P=1e5,那么这样的方法无法在合理时间内完成。 因此,更高效的方法是使用滑动窗口结合单调队列,时间复杂度为O(n)。 综上,使用缀和数组和单调队列的方法可能更优。 另一个思路是,对于每个可能的起始点i,计算i到i+P-1的和,直到i+Q-1,并记录最大值。这可能需要O(n*(Q-P))的时间,这在Q-P较大时不可行。 因此,最优解法应该是基于缀和和单调队列的,时间复杂度O(n)。 具体来说,步骤如下: 构造缀和数组pre,其中pre[0] = 0,pre[i] = pre[i-1] + arr[i-1](假设数组是0-based)。 对于每个右端点j,我们需要找到i的范围,使得子数组的长度在[P, Q]。即,子数组的起始点i的范围是j-Q+1 ≤ i ≤ j-P+1(假设子数组是i到j,所以长度是j-i+1)。因此,当固定j时,i必须满足i ≥ j - Q +1,且i ≤ j - P +1。例如,当j=5,Q=3,P=2时,子数组长度可以是2或3,对应的i=5-3+1=3到i=5-2+1=4,所以i的范围是3到4。对应的pre[j] - pre[i-1]即为子数组的和。 为了使pre[j] - pre[i-1]最大,我们需要在i的有效范围内找到最小的pre[i-1]。因此,对于每个j,我们需要在i的有效范围内找到最小的pre[i-1],这样pre[j]减去这个最小值就是当j对应的最大可能子数组和。 因此,我们可以维护一个单调队列,保存i的可能候选,其中队列中的i对应的pre[i]是递增的。这样,队列头部是当范围内pre[i]的最小值。 但是需要注意i的范围是随着j的变化而变化的。当处理到j时,有效的i范围是:[j-Q, j-P]。或者需要重新计算i的有效范围。例如,当j是当处理的位置,那么子数组的长度必须满足L >=P且 L <=Q。假设子数组的结束位置是j,起始位置是i,则子数组的长度是j -i +1。所以: j -i +1 >=P → i <= j -P +1 j -i +1 <=Q → i >= j -Q +1 所以i的有效范围是 [j-Q+1, j-P+1]。但是i必须>=0,并且j-P+1 >=0。因此,i的有效范围是max(0, j-Q+1) ≤i ≤ min(j-P+1, j). 比如,当P=2,Q=3,j=5: i >=5-3+1=3,i <=5-2+1=4。所以i可以是3或4。 对应的pre[i]是pre[3]和pre[4],因为子数组的和是pre[j] - pre[i-1]。例如,i=3时,子数组是i=3到j=5,长度是3,和为pre[5] - pre[2];i=4时,长度是2,和为pre[5]-pre[3]。 所以,对于每个j,我们需要在i的有效范围内找到最小的pre[i-1],其中i的取值范围是[j-Q+1, j-P+1]。然后,pre[j] - pre[i-1]就是这个j对应的可能的最大子数组和。 因此,我们可以维护一个单调队列,其中保存的是i的可能取值,且队列中的i对应的pre[i-1]是递增的。这样,当处理到j时,队列中的i的有效范围应该满足i >= j-Q+1且 i <=j-P+1。但如何维护这个队列? 可能的方法是,每次j增加时,确定当i的有效范围,然后将i的候选加入队列,并移除队列中超出左边界的i。比如,当j增加1时,新的i的有效范围是 [j-Q+1, j-P+1]。所以,当处理到j时,对应的i的有效范围的左边界是j-Q+1,右边界是j-P+1。因此,在处理j之,需要将队列中所有i < j-Q+1的元素移除。然后,将新的i = j-P+1对应的pre[i-1]加入队列,同时维护队列的递增顺序。例如,当j-P+1是一个有效的i时,对应的i是(j-P+1),那么对应的pre[i-1]是pre[j-P]。这可能吗? 或者,可能我的理解有误。这里可能需要更仔细的推导。 举个例子:假设当处理到j,此时i的有效范围是max(0, j-Q+1) ≤i ≤ j-P+1。对应的i的取值是这些整数。此时,对应的i-1的范围是max(-1, j-Q) ≤i-1 ≤j-P。而pre数组的索引是i-1,所以必须>=0。因此,i的有效范围实际上是i >=1时,i-1>=0。因此,可能i必须满足i >=1,即j-Q+1 >=1 → j >=Q-1。否则,i的取值可能为0,此时i-1=-1,但pre[-1]不存在。因此,可能我之的分析有错误。 这可能说明,我需要重新调整思路。例如,当i的取值范围是起始位置,那么子数组的起始位置i对应到pre数组中的索引是i的缀和是pre[i],那么子数组i到j的和是pre[j+1] - pre[i]。例如,原数组arr的索引是0到n-1,那么pre[0]=0,pre[1]=arr[0],pre[2]=arr[0]+arr[1],以此类推。因此,子数组arr[i..j]的和是pre[j+1] - pre[i]。 在这种情况下,子数组的长度是j -i +1。当长度必须位于[P, Q]之间时: j -i +1 >= P → i <= j -P +1 j -i +1 <= Q → i >= j -Q +1 所以i的有效范围是 [j-Q+1, j-P+1] 但i必须满足i >=0,并且j-P+1 >=0 → j >= P-1. 同时,i <= j-P+1 且 i >=j-Q+1。 现在,假设pre数组的长度是n+1,其中pre[0]=0,pre[k] = sum_{0<=m <k} arr[m}。 所以,对于每个j(作为子数组的结束位置),子数组的和是pre[j+1] - pre[i],其中i是子数组的起始位置,满足 j-Q+1 <=i <= j-P+1。 我们需要找到这个i的范围中,使得pre[i]最小,这样pre[j+1] - pre[i]就会最大。 因此,对于每个j,我们可以维护一个单调队列,保存i的可能值,其中队列中的i对应的pre[i]是递增的。当处理到j时,我们需要将i的可能值加入到队列中,并维护队列的有效范围。 具体来说: 1. 遍历j从0到n-1(原数组的索引),对应的pre[j+1]是到当j的缀和。 2. 对于每个j,对应的i的有效范围是i >= max(0, j - Q +1) 且 i <= j - P +1. 例如,当j=5,Q=3,P=2时,i的范围是5-3+1=3到5-2+1=4。即i=3和4。此时,子数组的起始位置i=3和4,对应的长度分别是5-3+1=3(符合Q=3)和5-4+1=2(符合P=2)。 但是原数组的长度是n,所以j的范围是0到n-1。所以当j=0时,可能无法满足P的条件,例如P=2的话,j=0时无法满足长度>=2,所以可能需要从j>=P-1开始处理。 3. 对于每个j,确定i的有效范围后,维护队列中的i,使得i在该范围内,并且pre[i]是递增的。 例如,当处理j时,i的有效范围的下界是i_min = max(0, j - Q +1),上界是i_max = j - P +1. 如果i_max <0,说明这个j无法满足长度>=P的条件,因此跳过。 否则,对于当j,我们需要将i_max对应的i(即i_max)加入队列。例如,当j增加时,i_max是新的i的可能候选。 另外,需要将队列中i小于i_min的元素都移除,因为它们不在当的有效范围内。 然后,队列中的头部元素就是当有效范围内pre[i]的最小值,从而得到最大的子数组和pre[j+1] - pre[i]。 因此,每次j的处理步骤可能如下: a. 计算i_max = j - P +1。如果i_max <0,说明无法满足长度>=P,跳过。 b. 计算i_min = j - Q +1. c. 将i_max对应的i加入队列。在加入之,需要将队列中所有比pre[i_max]大的元素的索引从队列尾部移除,保持队列递增。 d. 移除队列头部所有i < i_min的索引,因为它们不在当的有效范围内。 e. 如果队列不为空,当最大和为pre[j+1] - pre[队列头部元素],并更新全局最大值。 这样,每次j的处理需要维护队列,时间复杂度是O(n),因为每个i入队和出队各一次。 例如,当j递增时,i_max递增,因此每次可能将新的i_max加入队列。同时,i_min递增,导致队列头部的元素可能需要被移除。 现在,结合这个逻辑,代码的大致结构如下: 初始化pre数组,pre[0]=0,pre[k] = pre[k-1] + arr[k-1] 初始化双端队列deque,用来保存i的可能候选。 max_sum = 负无穷 遍历j从0到n-1: i_max = j - P +1 if i_max <0: continue # 将i_max加入队列 while deque不为空,并且pre[i_max] <= pre[deque的最后一个元素]: 移除最后一个元素 将i_max加入队列 # 现在,确定i_min = j - Q +1 i_min = j - Q +1 # 移除队列头部中所有i < i_min的元素 while deque不为空,并且deque的第一个元素 < i_min: 移除第一个元素 # 如果队列不为空,计算当最大值 if deque不为空: current_sum = pre[j+1] - pre[deque[0]] if current_sum > max_sum: max_sum = current_sum 这样,遍历完成后,max_sum即为所。 需要注意的问题: 1. 当P=0或者Q超过数组长度时,如何处理?但题目中给出的区间长度限制应该是正整数,并且P <=Q,所以假设P和Q满足1<=P<=Q<=n. 2. 需要处理当j-P+1超过数组长度的情况,比如j-P+1 >=n的情况。但pre数组的长度是n+1,所以i_max可能为n,但pre[n]是存在的。 举个例子,假设数组是[1,2,3,4], P=2, Q=3. pre数组是[0,1,3,6,10]. 当j=1(原数组索引是1,对应元素2),那么子数组的结束位置是j=1,对应pre[j+1] = pre[2] =3. i_max =1 -2 +1 =0. i_min =1-3+1= -1 → max(0, -1)=0. 此时,i的范围是0到0。对应i=0. pre[0] =0,所以pre[2] - pre[0] =3-0=3. 当j=2(元素3): i_max=2-2+1=1. i_min=2-3+1=0. 所以i的范围是0到1. 此时,i_max=1被加入队列。假设此时队列中有i=0和i=1的候选。pre[i=0]=0,i=1的pre是1. 当处理到i_max=1时,队列中原有i=0的pre是0,此时pre[1]=1>=0,所以i=1会被加入队列的尾部。队列现在是[0,1]. 然后i_min=0,所以队列头部是0,符合条件。此时计算pre[j+1] - pre[0] =6-0=6. 当j=3(元素4,索引3): i_max=3-2+1=2. i_min=3-3+1=1. 此时,i的范围是1到2. 将i=2加入队列。检查队列尾部,pre[2]=3 >=当pre[2]=3,所以移除尾部元素(可能没有),然后加入i=2。队列现在可能有i=0,1,2? 不,当处理到j=3时,之的i_max是2。在之的步骤中,处理j=2时,i_max是1。现在处理j=3时,i_max=3-2+1=2. 此时,需要将i=2加入队列。假设队列此时在处理j=2时,队列中有i=0,1,当j=3时,i_max=2. 处理i=2时,队列中的最后一个元素是i=1,对应的pre[i]=1。pre[2]=3 >=1,所以可以加入队列。队列变为[0,1,2]. 然后i_min=3-3+1=1. 所以需要移除队列中i<1的元素。队列头部i=0会被移除,剩下的队列是[1,2]. 此时pre[j+1]=pre[4]=10 - pre[1]=1 →10-1=9。这是当子数组起始于i=1,结束于j=3,长度3,和为9。或者起始于i=2,结束于j=3,长度2,和为7. 所以此时的最大是9,对应的i=1. 所以,这样的逻辑是正确的。 综上,代码的大致逻辑是: - 缀和数组pre. - 维护单调队列保存i,使得i对应的pre[i]递增。 - 对于每个j,处理i_max和i_min,维护队列,然后取出队列头部元素计算当最大值。 现在,编写代码时需要注意数组的索引问题。 例如,在Python中,原数组是arr,长度为n. pre的长度是n+1. pre[0] =0 pre[i] = pre[i-1] + arr[i-1] for i>=1. 当处理j为原数组的索引(从0到n-1),那么j+1是pre数组中的索引,pre[j+1]是到arr[j]的和。 此时,i的范围是i_max =j - P +1, i_min= j - Q +1. 那么,对应的i的取值必须满足i >=0,并且i <=j-P+1. 当i_max >=0时,才能处理。 因此,代码的框架可能如下: def max_subarray_sum(arr, P, Q): n = len(arr) pre = [0]*(n+1) for i in range(1, n+1): pre[i] = pre[i-1] + arr[i-1] from collections import deque dq = deque() max_sum = float('-inf') for j in range(n): i_max = j - P +1 if i_max <0: continue # 将i_max加入队列 while dq and pre[i_max] <= pre[dq[-1]]: dq.pop() dq.append(i_max) i_min = j - Q +1 # 移除队列中i < i_min的元素 while dq and dq[0] < i_min: dq.popleft() if dq: current_sum = pre[j+1] - pre[dq[0]] if current_sum > max_sum: max_sum = current_sum return max_sum 但是,这可能存在错误。例如,当处理每个j时,i_max可能超过pre数组的长度。例如,当j=5,P=2,n=5时,i_max=5-2+1=4,而pre的长度是6(索引0~5)。此时,i_max=4是有效的,因为pre[4]存在。 此外,当j增加时,i_max可能递增,比如当j=0时,i_max=0-2+1=-1,这会被跳过。 现在,测试一下这个代码是否正确。 例如,测试案例1: arr = [1,2,3,4], P=2, Q=3. n=4. pre数组是 [0,1,3,6,10]. 遍历j从0到3: j=0: i_max=0-2+1 =-1 → 跳过. j=1: i_max=1-2+1=0. i_min=1-3+1= -1 → max(0, -1)=0. 将i_max=0加入队列。此时队列为空,直接加入。队列:[0]. 移除队列中i<0的元素,无。当j+1=2 → pre[2]=3. current_sum=3 - pre[0]=3-0=3. max_sum=3. j=2: i_max=2-2+1=1. i_min=2-3+1=0. 处理: 将i_max=1加入队列。检查队列尾部,pre[1]=1 <=当pre[1]=1,所以弹出队列中的元素。队列原为[0],pre[0]=0。pre[1]=1>=0,所以队列弹出无元素,加入1。队列:[0,1]. 现在检查i_min=0。队列头部是0 >=0,所以保留。 current_sum = pre[3] - pre[0] =6-0=6. max_sum=6. j=3: i_max=3-2+1=2. i_min=3-3+1=1. 将i_max=2加入队列。队列尾部元素是1,pre[1]=1,pre[2]=3>=1 →弹出,加入2。队列变为[0,1,2]. 然后检查i_min=1。队列头部是0 <1,所以弹出。队列变为[1,2]. 头部是1 >=1. current_sum = pre[4]-pre[1]=10-1=9 → max_sum=9. 所以最终结果是9,正确。对应的子数组是索引1到3(元素2+3+4=9),长度3,符合Q=3. 另一个测试案例: arr = [-2, -3, 4, -1, 2, 1, -5, 3], P=2, Q=3. 可能的最大子数组是4, -1, 2,1 →长度4,但Q=3,所以可能只能选长度3的,比如4,-1,2 →和5,或者4-1+2+1=6? 但长度必须介于2和3之间。假设P=2,Q=3. 原数组的最大子数组和为6(4, -1,2,1),但长度是4,超过Q=3。所以需要找长度2或3中的最大子数组。 比如,子数组[4,-1,2]长度3,和为5;或者[2,1]长度2,和为3;或者[4,-1,2,1]长度4(不行),或者[4, -1,2]长度3,和为5;或者,可能存在其他子数组。 但根据我们的算法,正确的处理方式是找到每个可能的结束位置j,找到对应的i的范围,并计算最大值。例如,当j=2(元素4)时,i_max=2-2+1=1。i_min=2-3+1=0. 此时i可以是0或1. pre[j+1] =pre[3] = -2-3+4= -1. 所以 pre[3]-pre[0] =-1 - (-2) =1? 等等,可能这个例子需要重新计算。 可能这里需要更仔细的测试。 总之,上述代码的逻辑似乎正确,但需要更多测试。 另一个可能的错误是,当处理j时,可能i_max的值大于当pre数组的长度。比如,当j= n-1,i_max= (n-1) -P +1. 如果 P=1,那么i_max= n-1 -1 +1= n-1. 此时pre[i_max]是pre[n-1],存在。 综上,代码的大致思路是正确的。因此,可以通过这种方法实现,时间复杂度O(n),空间复杂度O(n)。</think>### 如何解限制区间长度的最大子数组和? 要解决给定区间长度在$P$到$Q$之间的最大子数组和问题,可以通过结合**缀和数组**和**单调队列**来高效实现,时间复杂度为$O(n)$,空间复杂度为$O(n)$。 #### 实现步骤 1. **构造缀和数组** 定义缀和数组$pre$,其中$pre[i]$表示原数组$i$个元素的和。例如,原数组为$arr$,则: $$ pre[0] = 0, \quad pre[i] = pre[i-1] + arr[i-1] $$ 通过缀和数组,可以快速计算任意区间$[i,j]$的和为$pre[j+1] - pre[i]$。 2. **维护单调队列** 使用双端队列(单调队列)保存可能的子数组起始点$i$,并确保队列中$i$对应的$pre[i]$是递增的。对于每个右端点$j$,需找到满足区间长度$P \leq j-i+1 \leq Q$的$i$,并从中选择最小的$pre[i]$以最大化子数组和。 3. **滑动窗口处理** 遍历每个右端点$j$时: - 计算$i$的有效范围:$i_{\text{max}} = j-P+1$(上限),$i_{\text{min}} = j-Q+1$(下限)。 - 将$i_{\text{max}}$加入队列,并移除队列中比$pre[i_{\text{max}}]$大的元素,保持队列递增。 - 移除队列中超出$i_{\text{min}}$范围的元素。 - 若队列非空,当最大和为$pre[j+1] - pre[\text{队首}]$,更新全局最大值。 #### 代码实现 ```python def max_subarray_sum(arr, P, Q): n = len(arr) pre = [0] * (n + 1) for i in range(1, n + 1): pre[i] = pre[i-1] + arr[i-1] from collections import deque dq = deque() max_sum = float('-inf') for j in range(n): i_max = j - P + 1 if i_max < 0: continue # 维护单调队列,保持pre[i]递增 while dq and pre[i_max] <= pre[dq[-1]]: dq.pop() dq.append(i_max) i_min = j - Q + 1 # 移除超出左边界的i while dq and dq[0] < i_min: dq.popleft() if dq: current_sum = pre[j+1] - pre[dq[0]] if current_sum > max_sum: max_sum = current_sum return max_sum ``` #### 示例分析 假设输入数组为$[1, 2, 3, 4]$,$P=2$,$Q=3$: 1. 缀和数组为$[0, 1, 3, 6, 10]$。 2. 当$j=3$(原数组索引3,对应元素4)时,$i_{\text{max}}=2$,$i_{\text{min}}=1$。队列中有效$i$为1和2,对应$pre[1]=1$和$pre[2]=3$。最大和为$pre[4]-pre[1]=10-1=9$,对应子数组$[2,3,4]$。 ### 相关问题 1. 如何优化单调队列的空间复杂度? 2. 如果数组包含负数,此方法是否仍然有效? 3. 如何扩展此方法以处理动态变化的数组?
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值