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

 
 
 
这题是很好的题目。
思考了很久一直没有想通。
后来突然理解了
可以参照大牛的解释:
这讲得比较详细了。
 
/*

 * 用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;

}

 

 

你可能感兴趣的:(sequence)