POJ 3017 Cut the Sequence【非决策单调性+单调队列+平衡树优化DP】

真的是一道非常棒的题目,我脑袋不怎么灵光,但是五个小时彻底弄懂还是相当值的!

Cut the Sequence
Time Limit: 2000MS Memory Limit: 131072K
Total Submissions: 11054 Accepted: 3382
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

POJ Monthly–2006.09.29, zhucheng

注意:POJ的数据水掉了,导致一些n^2复杂度的算法也可以通过,但是我们本着过的态度做题是毫无意义的。。。

我的LaTeX公式格式写得不好,所以就直接手写了。
这里写图片描述

有三点需要我们关注:
1.是否符合决策单调性——不符合。
2.如何维护区间最值——单调队列。
3.如何进行时间复杂度优化——平衡树。

本胖头鱼学长blog,LaTeX版本。http://www.cnblogs.com/zufezzt/p/8821194.html

在学长的指引下,终于理解了这道题,看来我的DP才刚刚入门!!!
下面用f数组代表dp数组。

#include<iostream>
#include<cstring>
#include<cstdio>
#include<algorithm>
#include<string>
#include<map>
#include<queue>
#include<vector>
#include<set>
using namespace std;
#define ll long long int
#define INF 0x3f3f3f3f
#define Pi acos(-1.0)
#define free(a) freopen(a".in","r",stdin),freopen(a".out","w",stdout);
const int maxn = 1e5 + 10;
int q[maxn], a[maxn], n;
ll m, sum[maxn], f[maxn];

int main()
{
    scanf("%d%lld", &n, &m);
    sum[0] = 0;
    for (int i = 1; i <= n; i++) {
        scanf("%d", &a[i]);
    }
    for (int i = 1; i <= n; i++) {
        sum[i] = sum[i - 1] + a[i];
        if (a[i] > m) {
            puts("-1"); return 0;
        }
    }
    multiset<ll> s; int l = 1, r = 0, k = 0;
    for (int i = 1; i <= n; i++) {
        while (sum[i] - sum[k]>m) {
            k++;
        }
        while (l <= r && q[l] <= k) {//维护的id在k之前,无效数据抹去
            if (r > l) {
                s.erase(f[q[l]] + a[q[l+1]]);
            }
            l++;
        }
        while (l <= r && a[i] >= a[q[r]]) {//维护递减单调队列
            if (r > l) {
                s.erase(f[q[r - 1]] + a[q[r]]);
            }
            r--;
        }
        q[++r] = i;
        f[i] = f[k] + a[q[l]]; ll tmp;
        if (l<r&&i>q[r - 1]) {
            s.insert(a[i] + f[q[r - 1]]);
        }
        if (s.size() && i > q[r - 1] && l < r) {
            tmp = *s.begin();
        }
        else {
            tmp = INF;
        }
        if (l < r)
            f[i] = min(f[i], tmp);
    }
    /*for (int i = 1; i <= n; i++) {
        printf("%lld ", f[i]);
    }*/
    printf("%lld\n", f[n]);
    return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值