POJ Cut the Sequence 单调队列优化DP入门题

这是一道关于使用单调队列优化动态规划的入门题目,要求将一个整数序列切割成连续子序列,每个子序列的和不超过给定整数M,并且要使所有部分的最大整数之和最小。输入包含序列长度N和限制值M,以及N个整数。输出为满足条件的切割方案中,最大整数之和的最小值,若不存在这样的切割,则输出-1。建议使用64位整数类型处理M。
摘要由CSDN通过智能技术生成

Cut the Sequence
Time Limit: 2000MS Memory Limit: 131072K
Total Submissions: 6478 Accepted: 1780

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


/*
 * File:   main.cpp
 * Author: hit-acm
 *
 * Created on 2012年5月22日, 下午11:44
 */

#include <iostream>
#include <algorithm>
#include <cstdio>
#include <cstring>
#include <cmath>
using namespace std;
#define MIN(A,B) (A<B)?A:B
/*
 * 此题的状态转移方程是很容易写的出来的,dp[i]=min(dp[j)+max(num[j+1->i])]),(sum(num[j+1->i])<=M);
 * 很明显是个n^2的DP,看一下数据范围必然超时,我们不得不想一下如何去优化此dp,使其复杂度变为至少是
 * nlgn级别的,如何去优化成为了一个难题。以我原来的知识是没有任何思路的。现在我能想的就是学习一种新
 * 的思想,就是用单调队列优化。看了很多神人的讲解之后,以此题作为入门学习的题目来进行学习。
 * 我们拿输入來举例:
 * 8 17
 * 2 2 2 8 1 8 2 1
 * 单调队列的变化形式为:
 * 第一个元素插入后:2
 * 第二个元素插入后:2
 * 第三个元素插入后:2
 * 第四个元素插入后:8
 * 第五个元素插入后:8 1
 * 第六个元素插入后:8
 * 第七个元素插入后:8 2
 * 第八个元素插入后:8 2 1
 *
 * 那么如何优化呢?
 * 现在我们先证明一个结论:
 * 有x<y,必然有:dp[x]<=dp[y]
 *(对于这个是很容易理解的,对于y的任何合法分组,x都可以通过
 * 构造,形成与其对应的分组。从而保证dp[x]<=dp[y]恒成立。
 * )。那么如果我们不考虑任意段的和不超过M这个限制条件的话,
 * 我们就可以利用dp[1]依次更新dp[3]一旦成功就可以直接break掉
 * 而不必,全部都更新一次。
 *
 * 关键是代码如何实现呢?
 * 我们可以设置一个st代表恰好满足:
 * sum[st,i]<=M.同时对单调队列进行更新,使过时的区间最大值消失
 * 那么用:dp[i]=dp[st-1]+当前单调队列的最大值,来更新但前的DP
 * 那么这就是结果么?不是的。比如4 5 3 M为 10.这种情况
 * dp[1]=4, dp[2]=5,dp[3]=dp[2]+3=8,如果按照上式就是:
 * dp[3]=dp[1]+5=9.
 * 为什么会出现这种情况呢?
 * 实际上对于单调队列中的情况如:
 * 8 2 1时。
 * 我们可以保证的是:
 * 8-2 ,2-1之间的满足。
 * 当x<y时,dp[x]+当前队列的最大值<=dp[y]+当前队列的最大值
 * 我们已经证明了:当x<y时,dp[x]<dp[y];
 * 又由于当前队列的最大值是相同的,因此不等式满足。
 * 因此,我们需要把单调队列里的所有备选方案全部拿出来对结果
 * 进行更新,那么得出的就是最优解。
 */

const int MAX = 100011;
long long a[MAX];
long long dp[MAX];
long long sum[MAX];
int p[MAX];
int start, end;

void insert(int x) {
    if (start > end) {
        start = end = 1;
        p[start] = x;
        return;
    }
    while (start <= end && a[p[end]] <= a[x]) end--;
    p[++end] = x;
}

void update(int st) {
    while (start <= end && st > p[start]) start++;
}

void init() {
    start = 1;
    end = 0;
}

int main() {
    int n;
    long long M;
    while (scanf("%d%lld", &n, &M) == 2) {
        init();
        sum[0] = 0;
        dp[0] = 0;
        int st = 1;
        bool flag = false;
        for (int i = 1; i <= n; i++) {
            scanf("%lld", &a[i]);
            if (!flag && a[i] > M) {
                flag = true;
            }
            sum[i] = sum[i - 1] + a[i];
            insert(i);
            while (sum[i] - sum[st - 1] > M) {
                st++;
            }
            update(st);
            dp[i] = dp[st - 1] + a[p[start]];
            for (int j = start; j < end; j++) {
                dp[i] = MIN(dp[i], dp[p[j]] + a[p[j + 1]]);
            }
        }
        if (flag) {
            printf("-1\n");
        } else {
            printf("%lld\n", dp[n]);
        }
    }
    return 0;
}


评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值