POJ-3017 Cut the Sequence(DP单调队列优化 + 平衡树)

185 篇文章 0 订阅
96 篇文章 0 订阅

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 containsN 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

题意:给定一个有 n个非负整数的数列 a,要求将其划分为若干个部分,使得每部分的和不超过给定的常数 m,并且所有部分的 最大值的和最小。其中 n<=10 5


分析:可以看出决策区间是具有单调性的,我们可以用一个双端队列来维护决策区间,但是这题最优决策在决策区间中的位置并不确定,而且最头的决策还需要特判,所以用一个set来维护最优决策的值。


#include <map>
#include <set>
#include <queue>
#include <string>
#include <vector>  
#include <cstdio>  
#include <utility>  
#include <cstring>  
#include <iostream>  
#include <algorithm>  
#define INF 0x3f3f3f3f
using namespace std; 
int n,tail;
long long m,sum[100007],a[100007],dp[100007];
deque <int> q ;
multiset <long long> f;
int main()
{
	scanf("%d%I64d",&n,&m);
	for(int i = 1;i <= n;i++) 
	{
		scanf("%I64d",&a[i]);
		sum[i] = sum[i-1] + a[i]; 
	}
	memset(dp,INF,sizeof(dp));
	dp[0] = 0;
	for(int i = 1;i <= n;i++)
	{
		while(sum[i] - sum[tail] > m) tail++;
		if(tail == i) 
		{
			cout<<-1<<endl;
			return 0;
		}
		while(!q.empty() && sum[i]-sum[q.front()-1] > m) 
		{
			deque <int> :: iterator it = q.begin();
			if(q.size() > 1) f.erase(dp[q.front()]+a[*++it]);
			q.pop_front();
		}
		while(!q.empty() && a[i] >= a[q.back()]) 
		{
			deque <int> :: iterator it = q.end();
			it--;
			if(q.size() > 1) f.erase(dp[*--it]+a[q.back()]);	
			q.pop_back();		
		}
		if(!q.empty()) f.insert(dp[q.back()]+a[i]);
		q.push_back(i);
		if(!f.empty()) dp[i] =	*f.begin();
		dp[i] = min(dp[i],dp[tail]+a[q.front()]);
	}
	cout<<dp[n]<<endl;
} 


  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值