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

Cut the Sequence

Time Limit: 2000MS Memory Limit: 131072K
Total Submissions: 7972 Accepted: 2287

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       这题是很好的题目。 思考了很久一直没有想通。 后来突然理解了 可以参照大牛的解释:
http://www.cnblogs.com/staginner/archive/2012/04/02/2429850.html 这讲得比较详细了。  

/*
 * 用dp[i]表示前i项进行划分得到的结果
 * dp[i]=min{dp[j],max[j+1,i]};
 * 首先假如不考虑m的限制。
 * 那么最优决策的那个j点,一定满足a[j] > max(a[j+1~i])就是j点的值要大于j+1到j点的值。
 * 很明显的结论,因为假如不成立,那么可以把j点也划分到后面去,得到更优的
 * 所以用单调队列维护一个递减数列     a1 a2 a3 a4  ...  a[i] ;a1>a2>a3>a4>a[i];
 * 这样的话最优决策点只能出现在这些点中。
 * 现在加了m的限制。假如上面那个递减数列只有后面一部分是满足m限制的 at,at+1,...a[i].
 * 那么决策点就是上面这些点再加上满足m条件的那个边界。
 * 所以可以用平衡二叉树维护
 * 二叉树里面的值只要随着队列进行维护就好了。
 * 可以用multiset实现。
 */

#include <iostream>
#include <string.h>
#include <algorithm>
#include <stdio.h>
#include <set>
using namespace std;
const int MAXN=100010;
int a[MAXN];
long long sum[MAXN];
int n;
long long m;
int q[MAXN];
int d[MAXN];
multiset<long long>st;
multiset<long long>::iterator it;
long long dp[MAXN];
void solve()
{
    int rear,front;
    rear=front=0;
    int decision=0;//限制条件
    st.clear();
    for(int i=1;i<=n;i++)
    {
        while(sum[i]-sum[decision]>m)decision++;
        while(front<rear && q[front]<=decision)//让不符合条件的退出队列
        {
            it=st.find(dp[d[front]]+a[q[front]]);
            st.erase(it);
            front++;
        }
        while(front<rear && a[i]>=a[q[rear-1]])//维护单调队列
        {
            it=st.find(dp[d[rear-1]]+a[q[rear-1]]);
            st.erase(it);
            rear--;
        }
        q[rear]=i;
        if(front<rear)d[rear]=q[rear-1];
        else d[rear]=decision;
        st.insert(dp[d[rear]]+a[i]);
        rear++;
        if(d[front]<decision)//更新那个限制点
        {
            it=st.find(dp[d[front]]+a[q[front]]);
            st.erase(it);
            d[front]=decision;
            st.insert(dp[decision]+a[q[front]]);
        }
        it=st.begin();//最小值
        dp[i]=(*it);
    }
    printf("%I64d\n",dp[n]);
}
int main()
{
//    freopen("in.txt","r",stdin);
//    freopen("out.txt","w",stdout);
    while(scanf("%d%I64d",&n,&m)==2)
    {
        bool flag=true;
        sum[0]=0;
        for(int i=1;i<=n;i++)
        {
            scanf("%d",&a[i]);
            sum[i]=sum[i-1]+a[i];
            if(a[i]>m)flag=false;
        }
        if(!flag)
        {
            printf("-1\n");
            continue;
        }
        solve();
    }
    return 0;
}

 

 

    原文作者:kuangbin
    原文地址: https://www.cnblogs.com/kuangbin/archive/2013/05/08/3066529.html
    本文转自网络文章,转载此文章仅为分享知识,如有侵权,请联系博主进行删除。
点赞