给定一个数列 {an} ,任意将连续的 ai 分块,使所有块的和都不超过 M ,最小化各块的最大值的和。
其中 ∑i−1k=jak≤M
时间复杂度是 O(n2) ,通过打表我们可以发现决策单调性(误),由于 dp[i] 是单调增的, ak 的最大值也是单调不降的,而且还得满足和不大于M,因此 j 是不减的,考虑单调队列。
开一个单调队列,使得队首到当前位置的和不超过m,那么单调队列里随便哪个元素都是满足要求的:
对于单调队列的最小值,set维护即可。
#include <cstdio>
#include <set>
using namespace std;
typedef long long ll;
const int N = 100005;
int q[N], dp[N], a[N];
multiset<int> s;
ll calc(int n, ll m) {
int i, j = 1, k, f = 0, r = 0; ll sum = 0;
for (i = 1; i <= n; ++i) {
sum += a[i];
while (sum > m) sum -= a[j++];
if (j > i) return -1;
while (f < r && a[q[r - 1]] <= a[i]) {
if (r - f >= 2) s.erase(dp[q[r - 2]] + a[q[r - 1]]);
--r;
}
q[r++] = i; if (r - f >= 2) s.insert(dp[q[r - 2]] + a[q[r - 1]]);
while (f < r && q[f] < j) {
if (r - f >= 2) s.erase(dp[q[f]] + a[q[f + 1]]);
++f;
}
dp[i] = dp[j - 1] + a[q[f]];
if (r - f >= 2) dp[i] = min(dp[i], *s.begin());
}
return dp[n];
}
int main() {
int i, n; ll m;
scanf("%d%lld", &n, &m);
for (i = 1; i <= n; ++i) scanf("%d", &a[i]);
return printf("%lld", calc(n, m)), 0;
}
Cut the Sequence
Time Limit: 2000MS Memory Limit: 131072K
Total Submissions: 9998 Accepted: 2999
Description
Given an integer sequence { an } of length N, you are to cut the sequence into several parts every one of which is a consecutive subsequence of the original sequence. Every part must satisfy that the sum of the integers in the part is not greater than a given integer M. You are to find a cutting that minimizes the sum of the maximum integer of each part.
Input
The first line of input contains two integer N (0 < N ≤ 100 000), M. The following line contains N integers describes the integer sequence. Every integer in the sequence is between 0 and 1 000 000 inclusively.
Output
Output one integer which is the minimum sum of the maximum integer of each part. If no such cuttings exist, output −1.
Sample Input
8 17
2 2 2 8 1 8 2 1
Sample Output
12
Hint
Use 64-bit integer type to hold M.
Source
POJ Monthly–2006.09.29, zhucheng