Cut the Sequence POJ - 3017(dp+单调队列优化)

思路:dp[i]表示前第i项的最小值。

很容易写出状态转移方程:dp[i]=min(dp[j]+max(a[j+1],a[i])),j<=i;

优化的思想是:我们需要快速的在前面找到一个j,同时还要找出j+1到i之间的最大项是什么,这样就能快速实现,最直接的思路是求RMQ,但是这题可以用单调队列。

用单调队列维护一个递减的序列,对于当前的a[i],先将队尾小于或等于a[i]的元素删掉,再把a[i]插入队尾。比如拿sample中的标号4-8一段8 1 8 2 1来看,那么队列里面存的标号就是6 7 8,对应的a[]的值就是8 2 1。

其实我们可以发现,在第6项以后的最值就是队列里存的编号7,换句话说,我们枚举一个j,那么下一个最值就是j+1对应的。

单调队列中的下一个元素就是后一段的最大值。

最后还要保证任何区间的和不超过m,这个只要保证队列的第一项到最后一项之和都不超过m,那么枚举的任何子区间都不会超过m。

参考博客:https://www.cnblogs.com/staginner/archive/2012/04/02/2429850.html

https://www.2cto.com/kf/201410/346223.html

代码:

#include<iostream>  
#include<cmath>  
#include<cstdio>  
#include<cstdlib>  
#include<string>  
#include<cstring>  
#include<algorithm>  
#include<vector>  
#include<map>  
#include<set>  
#include<stack>  
#include<list>  
#include<queue>  
#include <deque>
const int maxn=1e5+9;
typedef long long ll;
ll n,m;
ll dp[maxn];
ll a[maxn];
struct node
{
    int index;
    ll val;
}que[maxn];
ll min(ll i,ll j)
{
    return i<j?i:j;
}
int main(int argc, char const *argv[])
{
    #ifndef ONLINE_JUDGE
        freopen("in.txt","r",stdin);
        freopen("out.txt","w",stdout);
    #endif
    while(scanf("%lld%lld",&n,&m)!=EOF)
    {
        //sum[0]=0;
        bool f=0;
        for(int i=1;i<=n;i++)
        {
            scanf("%lld",&a[i]);
            if(a[i]>m) f=1;
            //sum[i]=sum[i-1]+a[i];
        }
        if(f==1)
        {
            printf("-1\n");
            continue;
        }
        int head,tail,pos;
        head=tail=0,pos=1;
        ll s=a[1];
        que[tail].val=dp[1]=a[1];
        que[tail++].index=1;
        for(int i=2;i<=n;i++)
        {
            s+=a[i];
            while(s>m&&pos<i)
            {
                s-=a[pos];
                pos++;
            }
            while(head<tail&&que[tail-1].val<=a[i])
            {
                tail--;
            }
            que[tail].val=a[i],que[tail++].index=i;
            while(que[head].index<pos&&head<tail)
            {
                head++;
            }
            dp[i]=dp[pos-1]+que[head].val;
            for(int j=head;j<tail-1;j++)
            {
                dp[i]=min(dp[i],dp[que[j].index]+que[j+1].val);
            }
        }
        printf("%lld\n",dp[n]);
    }
    return 0;
}

猜你喜欢

转载自blog.csdn.net/qq_40774175/article/details/82940671
今日推荐