[CF1304F] Animal Observation - dp,单调队列

[CF1304F] Animal Observation - dp,单调队列

设 \(f[i][j]\) 为第 \(i\) 天在第 \(j\) 个位置放置的最大值,设 \(s[i][j]\) 是第 \(i\) 行的前缀和,则
\[ \begin{align} f[i][j] & =s[i+1][j+k-1]-s[i+1][j-1]+ \\ \max_l & \begin{cases} f[i-1][l]+s[i][j+k-1]-s[i][j-1] & (1 \leq l \leq j-k) \\ f[i-1][l]+s[i][j+k-1]-s[i][l+k-1] & (j-k+1 \leq l \leq j) \\ f[i-1][l]+s[i][l-1]-s[i][j-1] & (j+1 \leq l \leq j+k-1) \\ f[i-1][l]+s[i][j+k-1]-s[i][j-1] & (j+k \leq l \leq m-k+1) \end{cases} \end{align} \]
如果暴力转移,则复杂度 \(O(nm^2 )\)

如果 \(k\) 很小,那么对中间两种情况暴力转移,旁边两种由于只有 \(f[i-1][l]\) 与 \(l\) 有关,可以预处理前后缀 \(\max\) 来解决,复杂度 \(O(nmk)\)

当 \(k\) 变大时,两侧的情况仍然暴力转移,中间的情况可以暴力用以 \(l\) 为下标的单调队列维护 \(f[i-1][l]-s[i][l+k-1]\) 和 \(f[i-1][l]+s[i][l-1]\)

(如果想偷懒也可以敲个线段树维护一下)

(发现单调队列优化DP不太熟练,准备要复习下)

#include <bits/stdc++.h>
using namespace std;

#define int long long
const int N = 55, M = 20005;

int a[N][M],s[N][M],f[N][M],n,m,k,q[M],qt[M],l,r;

signed main() {
    cin>>n>>m>>k;
    for(int i=1;i<=n;i++) {
        for(int j=1;j<=m;j++) {
            cin>>a[i][j];
            s[i][j]=s[i][j-1]+a[i][j];
        }
    }
    for(int i=1;i<=m-k+1;i++)
        f[1][i]=s[1][i+k-1]-s[1][i-1]+s[2][i+k-1]-s[2][i-1];
    for(int i=2;i<=n;i++) {
        for(int j=1;j<=m-k+1;j++)
            q[j]=f[i-1][j];
        for(int j=1;j<=m-k+1;j++)
            q[j]=max(q[j],q[j-1]);
        for(int j=k;j<=m-k+1;j++)
            f[i][j]=max(f[i][j],q[j-k]+s[i][j+k-1]-s[i][j-1]);
        for(int j=m-k+1;j;j--)
            q[j]=f[i-1][j];
        for(int j=m-k+1;j;j--)
            q[j]=max(q[j],q[j+1]);
        for(int j=1;j<=m-k+1;j++)
            f[i][j]=max(f[i][j],q[j+k]+s[i][j+k-1]-s[i][j-1]);
        l=1;r=0;
        for(int j=1;j<=m-k+1;j++) q[j]=-1e9;
        for(int j=1;j<=m-k+1;j++) {
            while(l<=r && q[r]<f[i-1][j]-s[i][j+k-1]) --r;
            ++r;
            q[r]=f[i-1][j]-s[i][j+k-1];
            qt[r]=j;
            while(l<=r && qt[l]<j-k+1) ++l;
            if(l<=r) f[i][j]=max(f[i][j],q[l]+s[i][j+k-1]);
        }
        l=1;r=0;
        for(int j=1;j<=m-k+1;j++) q[j]=-1e9;
        for(int j=m-k+1;j;--j) {
            while(l<=r && q[r]<f[i-1][j]+s[i][j-1]) --r;
            ++r;
            q[r]=f[i-1][j]+s[i][j-1];
            qt[r]=j;
            while(l<=r && qt[l]>j+k-1) ++l;
            if(l<=r) f[i][j]=max(f[i][j],q[l]-s[i][j-1]);
        }
        for(int j=1;j<=m-k+1;j++) f[i][j]+=s[i+1][j+k-1]-s[i+1][j-1];
    }
    int ans=0;
    /*for(int i=1;i<=n;i++) {
        for(int j=1;j<=m;j++) cout<<f[i][j]<<"\t";
        cout<<endl;
    }*/
    for(int i=1;i<=m;i++) ans=max(f[n][i],ans);
    cout<<ans;
}
上一篇:CF1304F Animal Observation(线段树+dp)


下一篇:Where Comes The Name "Softmax"?