[POJ]3017 单调队列 + set

本文介绍了一种名为CuttheSequence的算法问题,该问题要求将一个整数序列分割成若干连续子序列,使得每个子序列的和不超过给定值M,并且使各子序列最大值之和最小。文章详细解释了如何通过动态规划结合单调队列优化解决此问题。

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

Cut the Sequence
Time Limit: 2000MS Memory Limit: 131072K
Total Submissions: 10705 Accepted: 3267

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

[Submit]   [Go Back]   [Status]   [Discuss]

Home Page   Go Back  To top

   嗯...好题. 用到了单调性来优化复杂度.

   首先dp方程很容易得到. f[i] = min(f[j] + max(f[j + 1] + f[j + 2]....f[i])). 因为所有值都为非负数, 所以f[1] <= f[2] <= f[3]. 所以对于i来说, 如果j, j + 1, j + 2 ... k到i的最大值是相同的话, 肯定取f[j - 1](最大值是从j开始算的)是最优的. 而且又因为从1, 2, 3, ... i-1, i 到i的最大值肯定是单调不上升的, 那么用一个单调递减的维护最大值的位置, 然后当前 最大值+单调队列里上一个最大值的点的f(从上一个的之后最大值就是当前的, 所以相同最大值肯定要维护最后出现的, 所以单调递减, 而非不上升). 插入multiset里面求最小值即可(单调队列队首只是最大最大值,不代表是最优解) . 注意特判有点多. 

#include<stdio.h>
#include<set>
#ifdef WIN32
#define aut "%I64d"
#else
#define aut "%lld"
#endif
using namespace std;
multiset<int> s;
const int maxn = 100005;
typedef long long dnt;
bool flag;
int n, a[maxn], q[maxn];
dnt temp, f[maxn], m, sum;
int main(){
	scanf("%d"aut, &n, &m);
	flag = true;
	for(int i = 1; i <= n; ++i){
		scanf("%d", &a[i]);
		if(a[i] > m) flag = false;
	}
	if(!flag) {puts("-1"); return 0;}
	int low = 1, h = 1, t = 0;
	for(int i = 1; i <= n; ++i){
		sum += a[i];
		while(sum > m) sum -= a[low], ++low; 
		while(h <= t && a[q[t]] <= a[i]){
			if(t > h) s.erase(f[q[t - 1]] + a[q[t]]);
			--t;
		}
		q[++t] = i;
		if(t > h) s.insert(f[q[t - 1]] + a[q[t]]);
		while(low > q[h]){
			if(t > h) s.erase(f[q[h]] + a[q[h + 1]]);
			++h;
		}
		temp = *(s.begin()), f[i] =  f[low - 1] + a[q[h]];
		if(h < t && temp < f[i]) f[i] = temp; 
	}
	printf(aut, f[n]);
}



评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值