算法学习-单调队列优化系列

P1714 切蛋糕

题目描述

今天是小Z的生日,同学们为他带来了一块蛋糕。这块蛋糕是一个长方体,被用不同色彩分成了n个相同的小块,每小块都有对应的幸运值。

小Z作为寿星,自然希望吃到的蛋糕的幸运值总和最大,但小Z最多又只能吃m小块的蛋糕。

请你帮他从这n小块中找出连续的k块蛋糕,使得其上的总幸运值最大。

形式化地,在数列{p_n}中,找出一个子段[l,r] (r-l+1≤m),最大化∑(i=l to r) p_i。

输入格式

第一行两个整数n,m。分别代表共有n小块蛋糕,小Z最多只能吃m小块。

第二行n个整数,第i个整数p_i代表第i小块蛋糕的幸运值。

输出格式

仅一行一个整数,即小Z能够得到的最大幸运值。

输入输出样例

输入 #1

5 2
1 2 3 4 5

输出 #1

9

输入 #2

6 3
1 -2 3 -4 5 -6

输出 #2

5

说明/提示

数据规模与约定

  • 对于20%的数据,有1≤n≤100。
  • 对于100%的数据,有1≤n≤5×10^5,|p_i|≤500。

保证答案的绝对值在[0,2^31-1]之内。

解题思路:

转化为找不超过m的连续子序列, 使子序列的元素值和最大->o(nm) 超时....
想到可以维护一个固定长度为m的滑动窗口(单调序列),满足窗口内元素单调递增, 用deque实现 ->o(n) √

算法思路:

对于数组 a[1..n],求长度不超过 m 的最大子数组和。

定义前缀和:
S[i] = Σk=1i a[k],S[0] = 0

则问题可表示为:
max1≤l≤r≤n, r-l+1≤m Σk=lr a[k] = max1≤i≤n maxi-m≤j≤i-1 (S[i] - S[j])

进一步化简:
maxi-m≤j≤i-1 (S[i] - S[j]) = S[i] - mini-m≤j≤i-1 S[j]

因此,我们只需要用单调队列维护滑动窗口 [i-m, i-1] 中的最小 S[j] 即可。

代码呈现

点击查看代码
#include <iostream>
#include <vector>
#include <algorithm>
#include <cstring>
#include <queue>
#include <map>
#include <string>
#include <iomanip>
#include <cmath>
#include <numeric>
#include <climits>
#include <unordered_map>
#include <stack>
#include <utility>
#include <set>

#define ll long long
#define ull unsigned long long


struct node {
	ll sum, id, num;
};
const int N = 5e5 + 5;
const int MOD = 998244353;
const ll INF = 1e18;
int n, m;
ll a[N],sum[N];
std::deque<ll> q; 

void solve() {
// 维护一个固定长度为m的滑动窗口 满足窗口内元素单调递增 
	std::cin >> n >> m;
	for (int i = 1; i <= n; ++i) std::cin >> a[i];
	sum[0] = 0;
	for (int i = 1; i <= n; ++i) sum[i] = sum[i - 1] + a[i];
	ll max_val = -INT_MAX;
	q.push_back(0);
	for (int i = 1; i <= n; ++i) {
		if (!q.empty()) {
			max_val = std::max(max_val, sum[i] - sum[q.front()]);
		}
		while (!q.empty() && sum[q.back()]>=sum[i]) {
			q.pop_back();
		}
		q.push_back(i);
		if (!q.empty() && q.front() < i - m + 1) {
			q.pop_front();
		}
	}
	std::cout << max_val << std::endl;
}

int main() {
	std::ios::sync_with_stdio(false);
	std::cin.tie(0); std::cout.tie(0);

	int t = 1;
	// std::cin >> t;  // 多组数据别忘记初始化!!
	while (t--) {
		solve();
	}
	return 0;
}

后记:

此为我的第一篇博客,难耐激动之情,于此奉告!

posted @ 2025-12-04 21:29  QOKIK  阅读(0)  评论(0)    收藏  举报