poj3017 Cut the Sequence

97 篇文章 0 订阅
5 篇文章 0 订阅
Cut the Sequence
Time Limit:2000MS     Memory Limit:131072KB     64bit IO Format:%I64d & %I64u

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.

题目意思就是,给你一段数列,分成几个部分,要使每个部分的最大值的和最小,呵呵,听起来有点绕!

壮态转移方程f[i]=f[j]+max[j,r];但这题f[j]+max[j,r]可以用优先队列来存,其实要注意,优先队列,到底是存的是什么,优先队列,不能存入的是与现在状态改变,就变的值,你想如果这个值与i有关的话,当然要提出来,这是刚作单调队列的时候,有个小误区,当然这是后话,到时候,再分析,这还是很水的题啊!但这样,因为队首不是最优解,所以还是n*n的复杂度,用set保存,这样,查询的时候就是logn了,这样就降成了n*lgn 估计是数据太弱了,直接用n*n的竟然还快3倍多!

#include <iostream>
#include <set>
#include <stdio.h>
using namespace std;
#define MAXN 100500
__int64 queue[100000],num[MAXN],sum[MAXN];
__int64 dp[MAXN];
set<__int64 > myset;
set<__int64>::iterator po;
int fmin(int a,int b)
{
    if(a<b)return a;
    return b;
}
int main()
{
    int n,i,s,e,p;
    __int64 limit,ss;
    while(scanf("%d%I64d",&n,&limit)!=EOF)
    {
        sum[0]=0;
        myset.clear();
        for(i=1;i<=n;i++)
        {
            scanf("%I64d",&num[i]);
            sum[i]=sum[i-1]+num[i];
        }
        s=0,e=-1;p=1;ss=0;
        bool flag=true;
        for(i=1;i<=n;i++)
        {
            ss=ss+num[i];
            while(ss>limit)
            {
                ss=ss-num[p++];
            }
            if(p>i)
            {
                flag=false;
                break;
            }
            while(s<=e&&num[queue[e]]<=num[i])
            {
                if(s<e)
                myset.erase(dp[queue[e-1]]+num[queue[e]]);
                e--;

            }
            queue[++e]=i;
			if(s<e)
            myset.insert(dp[queue[e-1]]+num[queue[e]]);
            while(s<=e&&p>queue[s])
            {
                myset.erase(dp[queue[s]]+num[queue[s+1]]);
                s++;
            }
            po=myset.begin();
            dp[i]=dp[p-1]+num[queue[s]];
			if(s<e)
			{
				dp[i]=fmin(dp[i],*po);
			}
        }
        if(flag)
        {
            printf("%I64d\n",dp[n]);
        }
        else
        {
            printf("-1\n");
        }
    }
    return 0;
}




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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值