poj 3017

题意:给你n个数,求划分的区间所有的最大值之和(划分条件就是这个区间之和小于m),使其最小

首先很容易想到dp,公式 dp【i】= min(dp【i】,dp【j】+max(a【j+1】+。。。a【i】))

这是O(n^2)的复杂度

那么维护一个单调递减的序列的话,每次求的就是单调队列里面满足条件的那些元素,然后根据dp递推公式求解

Hint  :用多组交的话会wA

#include <cstdio>
#include <cstring>
#include <algorithm>
using namespace std;

#define ll long long
#define N 111111
const ll inf = 1111111111111;

ll sum;
ll a[N];
int n;
ll m;
int que[N];
ll dp[N];

ll slove(){
    sum = 0;
    int k  = 0;
    int l = 0;
    int r = -1;
    dp[0] = 0;
    for(int i=1;i<=n;i++){
        scanf("%I64d",&a[i]);
        if(a[i]>m) return -1;
        sum+=a[i];
        while(sum>m) sum-=a[++k]; //求满足区间和小于m的区间左端
        while(l<=r&&a[que[r]]<=a[i]) --r;//更新单调队列,即那些比a【i】小的元素都可以舍弃掉了,因为之后不会再用了
        que[++r] = i;
        while(l<=r&&que[l]<=k) l++;
        dp[i] = inf;
        int h = k;
       // printf(" h ---%d\n l--%d--r---%d\n%d\n",h,l,r,que[r]);
        for(int j=l;j<=r;j++){
            ll tmp = dp[h] + a[que[j]];
            if(tmp < dp[i]) dp[i] = tmp;
            h = que[j];
        }
    }
    return dp[n];
}

int main(){
    scanf("%d%I64d",&n,&m);
    printf("%I64d\n",slove());
}


你可能感兴趣的:(poj 3017)