poj 3017 Cut the Sequence(DP+单调队列+set)

Cut the Sequence
Time Limit: 2000MS Memory Limit: 131072K
Total Submissions: 7210 Accepted: 2025

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

题目:http://poj.org/problem?id=3017

题意:给你一个长度为n的数列,要求把这个数列划分为任意块,每块的元素和小于m,使得所有块的最大值的和最小

分析:这题很快就能想到一个DP方程 f[ i ]=min{ f[ j ] +max{ a[ k ] }}( b[ i ]<j<i,j<k<=i)     b[ i ]到 i的和大于m

这个方程的复杂度是O(n^2),明显要超时的(怎么discuss都说数据弱呢= =)

然后是优化了,首先当然是要优化一个最大值的队列,使得这个队列的队首元素的到当前位置的和不超过m,这样一个可行解就是,f[ i ]=f[b[ i ]-1]+a[ q[ l ]](即队首元素的值),这并不是最优解,所以还要找到队列中的最优解,一个可能的最优解只能是这样的 f[ q[ j ] ]+ a[ q[j +1 ]],也就是 a[ j ] 要大于后面的数,很显然,如果a[ j ]小于后面的数,那么我们就可以将 a[ j ] 划分到后面去,而取得更优解,这里涉及的这个找最优解问题,我想了很久,想找到一个O(1)的方式,但是始终找不到= =,最后还是放弃了,用了set,堕落了啊,没有手敲一棵平衡树。。。

代码:

#include<cstdio>
#include<set>
using namespace std;
const int mm=111111;
int a[mm],q[mm];
long long f[mm],sum,tmp,m;
multiset<int> sbt;
int i,l,r,p,n;
int main()
{
    scanf("%d%I64d",&n,&m);
    sbt.clear();
    sum=l=0,f[n]=r=-1;
    for(p=i=1;i<=n;++i)
    {
        scanf("%d",&a[i]);
        sum+=a[i];
        while(sum>m)sum-=a[p++];
        if(p>i)break;
        while(l<=r&&a[i]>=a[q[r]])
        {
            if(l<r)sbt.erase(f[q[r-1]]+a[q[r]]);
            --r;
        }
        q[++r]=i;
        if(l<r)sbt.insert(f[q[r-1]]+a[q[r]]);
        while(q[l]<p)
        {
            if(l<r)sbt.erase(f[q[l]]+a[q[l+1]]);
            ++l;
        }
        f[i]=f[p-1]+a[q[l]];
        tmp=*sbt.begin();
        if(l<r&&f[i]>tmp)f[i]=tmp;
    }
    printf("%I64d\n",f[n]);
    return 0;
}

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值