单调队列优化

· · 个人记录

概述

单调队列

单调队列优化

实现原理

For(i,1,n){
    while(hd<=tl && q[hd]<i-k) ++hd;//k是长度限制。
//注意,这里是i-k,因为i可以获得一个所谓的长为“k”的跨度转移过来,这个跨度是不包含转移起始点的。当然,我们要就实际情况讨论。
    dp[i]=dp[q[hd]]+(particular check);
    while(hd<=tl && (particular check) --tl;
    q[++tl]=i;
}

例题

P5202 [USACO19JAN]Redistricting P

For(i,1,n){
    while(hd<=tl && q[hd]<i-k) ++hd;
    dp[i]=dp[q[hd]]+(sum[i]-sum[q[hd]]<=0);
    while(hd<=tl && (dp[q[tl]]>dp[i] || (dp[q[tl]]==dp[i] && sum[i]<sum[q[tl]]) ) ) --tl;
    q[++tl]=i;
}

单调队列优化多重背包

dp_{i,j}=\max_{k=0}^{num_i} (dp_{i-1,j-k\times c_i}+k\times w_i)
int n;
struct goods{
    int c,w,num;
}a[maxn];
int dp[maxn][maxn];
int sum[maxn];
struct from{
    int k,v,id;//k 是实际的 ∑c,v 是 dp 值,id 是剩余系下编号 
    from(){}
    from(int _k,int _v,int _id){k=_k,v=_v,id=_id;}
}q[maxn];
int hd,tl;

il void DP(){
    int rg; q[0].k=-inf;//目的是总是能在 hd=0 时 ++hd。通过控制 id 来把 tl 减成负的显然不明智,那会导致 q[0] 有值。
    For(i,1,n){
        rg=a[i].c*a[i].num; sum[i]=sum[i-1]+rg; if(sum[i]>uselim) sum[i]=uselim;
        for(int j=0;j<a[i].c;++j){
            for(int k=j,idn=0;k<=sum[i];k+=a[i].c,++idn){
                while(hd<=tl && q[hd].k+rg<k) ++hd;
                while(hd<=tl && q[tl].v+(idn-q[tl].id)*a[i].w<=dp[i-1][k]) --tl;
                q[++tl]=from(k,dp[i-1][k],idn);

                dp[i][k]=q[hd].v+(idn-q[hd].id)*a[i].w;
            }
            hd=tl=0;
        }
        For(j,1,uselim) chkmax(dp[i][j],dp[i][j-1]);
    }
}