给定一个长度为 N 的序列 A ,要求把该序列分成若干段,在满足“每段中所有数的和”不超过M的前提下,让“每段中所有数的最大值”之和最小。
试计算这个最小值。
输入格式
第一行包含两个整数N和M。
第二行包含N个整数,表示完整的序列A。
输出格式
输出一个整数,表示结果。
如果结果不存在,则输出-1。
数据范围
0≤N≤10^5,
0≤M≤10^11,
序列A中的数非负,且不超过10^6
输入样例:
8 17
2 2 2 8 1 8 2 1
输出样例:
12
#include<bits/stdc++.h>
#define ll long long
using namespace std;
const int N = 1e5 + 10;
ll f[N], m, sum;
int a[N], n;
deque<int> q;
int main() {
scanf("%d%lld", &n, &m);
for (int i = 1; i <= n; i++) {
scanf("%d", &a[i]);
if (a[i] > m) {
puts("-1");
return 0;
}
}
for (int i = 1, j = 0; i <= n; i++) {
sum += a[i];
while (sum > m)sum -= a[++j];
while (!q.empty() && q.front() <= j)q.pop_front();
while (!q.empty() && a[q.back()] <= a[i])q.pop_back();
q.push_back(i);
f[i] = f[j] + a[q.front()];
auto en = --q.end();
for (auto it = q.begin(); it != en;)
f[i] = min(f[i], f[*it] + a[*(++it)]);
}
printf("%lld\n", f[n]);
return 0;
}
优化版
#include<bits/stdc++.h>
#define ll long long
using namespace std;
const int N = 1e5 + 10;
ll f[N], m, sum;
int a[N], n;
deque<int> q;
multiset<ll> heap;
multiset<ll>::iterator it;
int main() {
scanf("%d%lld", &n, &m);
for (int i = 1; i <= n; i++) {
scanf("%d", &a[i]);
if (a[i] > m) {
puts("-1");
return 0;
}
}
for (int i = 1, j = 0; i <= n; i++) {
sum += a[i];
while (sum > m)sum -= a[++j];
while (!q.empty() && q.front() <= j) {
if (q.size() > 1 && (it = heap.find(f[q.front()] + a[*(++q.begin())])) != heap.end())
heap.erase(it);
q.pop_front();
}
while (!q.empty() && a[q.back()] <= a[i]) {
if (q.size() > 1 && (it = heap.find(f[*(-- --q.end())] + a[q.back()])) != heap.end())
heap.erase(it);
q.pop_back();
}
if (!q.empty())heap.insert(f[q.back()] + a[i]);
q.push_back(i);
f[i] = f[j] + a[q.front()];
if (!heap.empty())f[i] = min(f[i], *heap.begin());
}
printf("%lld\n", f[n]);
return 0;
}